diff --git a/__pycache__/DuckDuckGo.cpython-38.pyc b/__pycache__/DuckDuckGo.cpython-38.pyc
deleted file mode 100644
index 0cdca19..0000000
Binary files a/__pycache__/DuckDuckGo.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER b/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER
deleted file mode 100644
index a1b589e..0000000
--- a/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst b/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst
deleted file mode 100644
index 9d227a0..0000000
--- a/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright 2010 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/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/METADATA b/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/METADATA
deleted file mode 100644
index dfe37d5..0000000
--- a/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/METADATA
+++ /dev/null
@@ -1,93 +0,0 @@
-Metadata-Version: 2.1
-Name: MarkupSafe
-Version: 2.1.5
-Summary: Safely add untrusted strings to HTML/XML markup.
-Home-page: https://palletsprojects.com/p/markupsafe/
-Maintainer: Pallets
-Maintainer-email: contact@palletsprojects.com
-License: BSD-3-Clause
-Project-URL: Donate, https://palletsprojects.com/donate
-Project-URL: Documentation, https://markupsafe.palletsprojects.com/
-Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
-Project-URL: Source Code, https://github.com/pallets/markupsafe/
-Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/
-Project-URL: Chat, https://discord.gg/pallets
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Environment :: Web Environment
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: BSD License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
-Classifier: Topic :: Text Processing :: Markup :: HTML
-Requires-Python: >=3.7
-Description-Content-Type: text/x-rst
-License-File: LICENSE.rst
-
-MarkupSafe
-==========
-
-MarkupSafe implements a text object that escapes characters so it is
-safe to use in HTML and XML. Characters that have special meanings are
-replaced so that they display as the actual characters. This mitigates
-injection attacks, meaning untrusted user input can safely be displayed
-on a page.
-
-
-Installing
-----------
-
-Install and update using `pip`_:
-
-.. code-block:: text
-
- pip install -U MarkupSafe
-
-.. _pip: https://pip.pypa.io/en/stable/getting-started/
-
-
-Examples
---------
-
-.. code-block:: pycon
-
- >>> from markupsafe import Markup, escape
-
- >>> # escape replaces special characters and wraps in Markup
- >>> escape("")
- Markup('<script>alert(document.cookie);</script>')
-
- >>> # wrap in Markup to mark text "safe" and prevent escaping
- >>> Markup("Hello")
- Markup('hello')
-
- >>> escape(Markup("Hello"))
- Markup('hello')
-
- >>> # Markup is a str subclass
- >>> # methods and operators escape their arguments
- >>> template = Markup("Hello {name}")
- >>> template.format(name='"World"')
- Markup('Hello "World"')
-
-
-Donate
-------
-
-The Pallets organization develops and supports MarkupSafe 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://markupsafe.palletsprojects.com/
-- Changes: https://markupsafe.palletsprojects.com/changes/
-- PyPI Releases: https://pypi.org/project/MarkupSafe/
-- Source Code: https://github.com/pallets/markupsafe/
-- Issue Tracker: https://github.com/pallets/markupsafe/issues/
-- Chat: https://discord.gg/pallets
diff --git a/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/RECORD b/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/RECORD
deleted file mode 100644
index 4b72c7c..0000000
--- a/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/RECORD
+++ /dev/null
@@ -1,14 +0,0 @@
-MarkupSafe-2.1.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-MarkupSafe-2.1.5.dist-info/LICENSE.rst,sha256=RjHsDbX9kKVH4zaBcmTGeYIUM4FG-KyUtKV_lu6MnsQ,1503
-MarkupSafe-2.1.5.dist-info/METADATA,sha256=icNlaniV7YIQZ1BScCVqNaRtm7MAgfw8d3OBmoSVyAY,3096
-MarkupSafe-2.1.5.dist-info/RECORD,,
-MarkupSafe-2.1.5.dist-info/WHEEL,sha256=3SeyPJ5-Us2Ct5GSftUVKtLSlm-bNefW4m5qd0GLzww,100
-MarkupSafe-2.1.5.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11
-markupsafe/__init__.py,sha256=m1ysNeqf55zbEoJtaovca40ivrkEFolPlw5bGoC5Gi4,11290
-markupsafe/__pycache__/__init__.cpython-38.pyc,,
-markupsafe/__pycache__/_native.cpython-38.pyc,,
-markupsafe/_native.py,sha256=_Q7UsXCOvgdonCgqG3l5asANI6eo50EKnDM-mlwEC5M,1776
-markupsafe/_speedups.c,sha256=n3jzzaJwXcoN8nTFyA53f3vSqsWK2vujI-v6QYifjhQ,7403
-markupsafe/_speedups.cp38-win_amd64.pyd,sha256=SZNUyQtXKMzLDnnjHrCTzUx2gn7cSs9PtqHylpKLgWM,15872
-markupsafe/_speedups.pyi,sha256=f5QtwIOP0eLrxh2v5p6SmaYmlcHIGIfmz0DovaqL0OU,238
-markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
diff --git a/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL b/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL
deleted file mode 100644
index f93f57f..0000000
--- a/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL
+++ /dev/null
@@ -1,5 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.42.0)
-Root-Is-Purelib: false
-Tag: cp38-cp38-win_amd64
-
diff --git a/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt b/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt
deleted file mode 100644
index 75bf729..0000000
--- a/myenv/Lib/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-markupsafe
diff --git a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/INSTALLER b/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/INSTALLER
deleted file mode 100644
index a1b589e..0000000
--- a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/LICENSE b/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/LICENSE
deleted file mode 100644
index 04b6b1f..0000000
--- a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright 2006 Dan-Haim. 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 Dan Haim nor the names of his contributors may be used
- to endorse or promote products derived from this software without specific
- prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY DAN HAIM "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 DAN HAIM OR HIS 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, 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 DAMANGE.
diff --git a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/METADATA b/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/METADATA
deleted file mode 100644
index ae2ae34..0000000
--- a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/METADATA
+++ /dev/null
@@ -1,321 +0,0 @@
-Metadata-Version: 2.1
-Name: PySocks
-Version: 1.7.1
-Summary: A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information.
-Home-page: https://github.com/Anorov/PySocks
-Author: Anorov
-Author-email: anorov.vorona@gmail.com
-License: BSD
-Keywords: socks,proxy
-Platform: UNKNOWN
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
-Description-Content-Type: text/markdown
-
-PySocks
-=======
-
-PySocks lets you send traffic through SOCKS and HTTP proxy servers. It is a modern fork of [SocksiPy](http://socksipy.sourceforge.net/) with bug fixes and extra features.
-
-Acts as a drop-in replacement to the socket module. Seamlessly configure SOCKS proxies for any socket object by calling `socket_object.set_proxy()`.
-
-----------------
-
-Features
-========
-
-* SOCKS proxy client for Python 2.7 and 3.4+
-* TCP supported
-* UDP mostly supported (issues may occur in some edge cases)
-* HTTP proxy client included but not supported or recommended (you should use urllib2's or requests' own HTTP proxy interface)
-* urllib2 handler included. `pip install` / `setup.py install` will automatically install the `sockshandler` module.
-
-Installation
-============
-
- pip install PySocks
-
-Or download the tarball / `git clone` and...
-
- python setup.py install
-
-These will install both the `socks` and `sockshandler` modules.
-
-Alternatively, include just `socks.py` in your project.
-
---------------------------------------------
-
-*Warning:* PySocks/SocksiPy only supports HTTP proxies that use CONNECT tunneling. Certain HTTP proxies may not work with this library. If you wish to use HTTP (not SOCKS) proxies, it is recommended that you rely on your HTTP client's native proxy support (`proxies` dict for `requests`, or `urllib2.ProxyHandler` for `urllib2`) instead.
-
---------------------------------------------
-
-Usage
-=====
-
-## socks.socksocket ##
-
- import socks
-
- s = socks.socksocket() # Same API as socket.socket in the standard lib
-
- s.set_proxy(socks.SOCKS5, "localhost") # SOCKS4 and SOCKS5 use port 1080 by default
- # Or
- s.set_proxy(socks.SOCKS4, "localhost", 4444)
- # Or
- s.set_proxy(socks.HTTP, "5.5.5.5", 8888)
-
- # Can be treated identical to a regular socket object
- s.connect(("www.somesite.com", 80))
- s.sendall("GET / HTTP/1.1 ...")
- print s.recv(4096)
-
-## Monkeypatching ##
-
-To monkeypatch the entire standard library with a single default proxy:
-
- import urllib2
- import socket
- import socks
-
- socks.set_default_proxy(socks.SOCKS5, "localhost")
- socket.socket = socks.socksocket
-
- urllib2.urlopen("http://www.somesite.com/") # All requests will pass through the SOCKS proxy
-
-Note that monkeypatching may not work for all standard modules or for all third party modules, and generally isn't recommended. Monkeypatching is usually an anti-pattern in Python.
-
-## urllib2 Handler ##
-
-Example use case with the `sockshandler` urllib2 handler. Note that you must import both `socks` and `sockshandler`, as the handler is its own module separate from PySocks. The module is included in the PyPI package.
-
- import urllib2
- import socks
- from sockshandler import SocksiPyHandler
-
- opener = urllib2.build_opener(SocksiPyHandler(socks.SOCKS5, "127.0.0.1", 9050))
- print opener.open("http://www.somesite.com/") # All requests made by the opener will pass through the SOCKS proxy
-
---------------------------------------------
-
-Original SocksiPy README attached below, amended to reflect API changes.
-
---------------------------------------------
-
-SocksiPy
-
-A Python SOCKS module.
-
-(C) 2006 Dan-Haim. All rights reserved.
-
-See LICENSE file for details.
-
-
-*WHAT IS A SOCKS PROXY?*
-
-A SOCKS proxy is a proxy server at the TCP level. In other words, it acts as
-a tunnel, relaying all traffic going through it without modifying it.
-SOCKS proxies can be used to relay traffic using any network protocol that
-uses TCP.
-
-*WHAT IS SOCKSIPY?*
-
-This Python module allows you to create TCP connections through a SOCKS
-proxy without any special effort.
-It also supports relaying UDP packets with a SOCKS5 proxy.
-
-*PROXY COMPATIBILITY*
-
-SocksiPy is compatible with three different types of proxies:
-
-1. SOCKS Version 4 (SOCKS4), including the SOCKS4a extension.
-2. SOCKS Version 5 (SOCKS5).
-3. HTTP Proxies which support tunneling using the CONNECT method.
-
-*SYSTEM REQUIREMENTS*
-
-Being written in Python, SocksiPy can run on any platform that has a Python
-interpreter and TCP/IP support.
-This module has been tested with Python 2.3 and should work with greater versions
-just as well.
-
-
-INSTALLATION
--------------
-
-Simply copy the file "socks.py" to your Python's `lib/site-packages` directory,
-and you're ready to go. [Editor's note: it is better to use `python setup.py install` for PySocks]
-
-
-USAGE
-------
-
-First load the socks module with the command:
-
- >>> import socks
- >>>
-
-The socks module provides a class called `socksocket`, which is the base to all of the module's functionality.
-
-The `socksocket` object has the same initialization parameters as the normal socket
-object to ensure maximal compatibility, however it should be noted that `socksocket` will only function with family being `AF_INET` and
-type being either `SOCK_STREAM` or `SOCK_DGRAM`.
-Generally, it is best to initialize the `socksocket` object with no parameters
-
- >>> s = socks.socksocket()
- >>>
-
-The `socksocket` object has an interface which is very similiar to socket's (in fact
-the `socksocket` class is derived from socket) with a few extra methods.
-To select the proxy server you would like to use, use the `set_proxy` method, whose
-syntax is:
-
- set_proxy(proxy_type, addr[, port[, rdns[, username[, password]]]])
-
-Explanation of the parameters:
-
-`proxy_type` - The type of the proxy server. This can be one of three possible
-choices: `PROXY_TYPE_SOCKS4`, `PROXY_TYPE_SOCKS5` and `PROXY_TYPE_HTTP` for SOCKS4,
-SOCKS5 and HTTP servers respectively. `SOCKS4`, `SOCKS5`, and `HTTP` are all aliases, respectively.
-
-`addr` - The IP address or DNS name of the proxy server.
-
-`port` - The port of the proxy server. Defaults to 1080 for socks and 8080 for http.
-
-`rdns` - This is a boolean flag than modifies the behavior regarding DNS resolving.
-If it is set to True, DNS resolving will be preformed remotely, on the server.
-If it is set to False, DNS resolving will be preformed locally. Please note that
-setting this to True with SOCKS4 servers actually use an extension to the protocol,
-called SOCKS4a, which may not be supported on all servers (SOCKS5 and http servers
-always support DNS). The default is True.
-
-`username` - For SOCKS5 servers, this allows simple username / password authentication
-with the server. For SOCKS4 servers, this parameter will be sent as the userid.
-This parameter is ignored if an HTTP server is being used. If it is not provided,
-authentication will not be used (servers may accept unauthenticated requests).
-
-`password` - This parameter is valid only for SOCKS5 servers and specifies the
-respective password for the username provided.
-
-Example of usage:
-
- >>> s.set_proxy(socks.SOCKS5, "socks.example.com") # uses default port 1080
- >>> s.set_proxy(socks.SOCKS4, "socks.test.com", 1081)
-
-After the set_proxy method has been called, simply call the connect method with the
-traditional parameters to establish a connection through the proxy:
-
- >>> s.connect(("www.sourceforge.net", 80))
- >>>
-
-Connection will take a bit longer to allow negotiation with the proxy server.
-Please note that calling connect without calling `set_proxy` earlier will connect
-without a proxy (just like a regular socket).
-
-Errors: Any errors in the connection process will trigger exceptions. The exception
-may either be generated by the underlying socket layer or may be custom module
-exceptions, whose details follow:
-
-class `ProxyError` - This is a base exception class. It is not raised directly but
-rather all other exception classes raised by this module are derived from it.
-This allows an easy way to catch all proxy-related errors. It descends from `IOError`.
-
-All `ProxyError` exceptions have an attribute `socket_err`, which will contain either a
-caught `socket.error` exception, or `None` if there wasn't any.
-
-class `GeneralProxyError` - When thrown, it indicates a problem which does not fall
-into another category.
-
-* `Sent invalid data` - This error means that unexpected data has been received from
-the server. The most common reason is that the server specified as the proxy is
-not really a SOCKS4/SOCKS5/HTTP proxy, or maybe the proxy type specified is wrong.
-
-* `Connection closed unexpectedly` - The proxy server unexpectedly closed the connection.
-This may indicate that the proxy server is experiencing network or software problems.
-
-* `Bad proxy type` - This will be raised if the type of the proxy supplied to the
-set_proxy function was not one of `SOCKS4`/`SOCKS5`/`HTTP`.
-
-* `Bad input` - This will be raised if the `connect()` method is called with bad input
-parameters.
-
-class `SOCKS5AuthError` - This indicates that the connection through a SOCKS5 server
-failed due to an authentication problem.
-
-* `Authentication is required` - This will happen if you use a SOCKS5 server which
-requires authentication without providing a username / password at all.
-
-* `All offered authentication methods were rejected` - This will happen if the proxy
-requires a special authentication method which is not supported by this module.
-
-* `Unknown username or invalid password` - Self descriptive.
-
-class `SOCKS5Error` - This will be raised for SOCKS5 errors which are not related to
-authentication.
-The parameter is a tuple containing a code, as given by the server,
-and a description of the
-error. The possible errors, according to the RFC, are:
-
-* `0x01` - General SOCKS server failure - If for any reason the proxy server is unable to
-fulfill your request (internal server error).
-* `0x02` - connection not allowed by ruleset - If the address you're trying to connect to
-is blacklisted on the server or requires authentication.
-* `0x03` - Network unreachable - The target could not be contacted. A router on the network
-had replied with a destination net unreachable error.
-* `0x04` - Host unreachable - The target could not be contacted. A router on the network
-had replied with a destination host unreachable error.
-* `0x05` - Connection refused - The target server has actively refused the connection
-(the requested port is closed).
-* `0x06` - TTL expired - The TTL value of the SYN packet from the proxy to the target server
-has expired. This usually means that there are network problems causing the packet
-to be caught in a router-to-router "ping-pong".
-* `0x07` - Command not supported - For instance if the server does not support UDP.
-* `0x08` - Address type not supported - The client has provided an invalid address type.
-When using this module, this error should not occur.
-
-class `SOCKS4Error` - This will be raised for SOCKS4 errors. The parameter is a tuple
-containing a code and a description of the error, as given by the server. The
-possible error, according to the specification are:
-
-* `0x5B` - Request rejected or failed - Will be raised in the event of an failure for any
-reason other then the two mentioned next.
-* `0x5C` - request rejected because SOCKS server cannot connect to identd on the client -
-The Socks server had tried an ident lookup on your computer and has failed. In this
-case you should run an identd server and/or configure your firewall to allow incoming
-connections to local port 113 from the remote server.
-* `0x5D` - request rejected because the client program and identd report different user-ids -
-The Socks server had performed an ident lookup on your computer and has received a
-different userid than the one you have provided. Change your userid (through the
-username parameter of the set_proxy method) to match and try again.
-
-class `HTTPError` - This will be raised for HTTP errors. The message will contain
-the HTTP status code and provided error message.
-
-After establishing the connection, the object behaves like a standard socket.
-
-Methods like `makefile()` and `settimeout()` should behave just like regular sockets.
-Call the `close()` method to close the connection.
-
-In addition to the `socksocket` class, an additional function worth mentioning is the
-`set_default_proxy` function. The parameters are the same as the `set_proxy` method.
-This function will set default proxy settings for newly created `socksocket` objects,
-in which the proxy settings haven't been changed via the `set_proxy` method.
-This is quite useful if you wish to force 3rd party modules to use a SOCKS proxy,
-by overriding the socket object.
-For example:
-
- >>> socks.set_default_proxy(socks.SOCKS5, "socks.example.com")
- >>> socket.socket = socks.socksocket
- >>> urllib.urlopen("http://www.sourceforge.net/")
-
-
-PROBLEMS
----------
-
-Please open a GitHub issue at https://github.com/Anorov/PySocks
-
-
diff --git a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/RECORD b/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/RECORD
deleted file mode 100644
index 638948d..0000000
--- a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/RECORD
+++ /dev/null
@@ -1,10 +0,0 @@
-PySocks-1.7.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-PySocks-1.7.1.dist-info/LICENSE,sha256=cCfiFOAU63i3rcwc7aWspxOnn8T2oMUsnaWz5wfm_-k,1401
-PySocks-1.7.1.dist-info/METADATA,sha256=zbQMizjPOOP4DhEiEX24XXjNrYuIxF9UGUpN0uFDB6Y,13235
-PySocks-1.7.1.dist-info/RECORD,,
-PySocks-1.7.1.dist-info/WHEEL,sha256=t_MpApv386-8PVts2R6wsTifdIn0vbUDTVv61IbqFC8,92
-PySocks-1.7.1.dist-info/top_level.txt,sha256=TKSOIfCFBoK9EY8FBYbYqC3PWd3--G15ph9n8-QHPDk,19
-__pycache__/socks.cpython-38.pyc,,
-__pycache__/sockshandler.cpython-38.pyc,,
-socks.py,sha256=xOYn27t9IGrbTBzWsUUuPa0YBuplgiUykzkOB5V5iFY,31086
-sockshandler.py,sha256=2SYGj-pwt1kjgLoZAmyeaEXCeZDWRmfVS_QG6kErGtY,3966
diff --git a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/WHEEL b/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/WHEEL
deleted file mode 100644
index 129a673..0000000
--- a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/WHEEL
+++ /dev/null
@@ -1,5 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.33.3)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
diff --git a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/top_level.txt b/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/top_level.txt
deleted file mode 100644
index 9476163..0000000
--- a/myenv/Lib/site-packages/PySocks-1.7.1.dist-info/top_level.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-socks
-sockshandler
diff --git a/myenv/Lib/site-packages/__pycache__/easy_install.cpython-38.pyc b/myenv/Lib/site-packages/__pycache__/easy_install.cpython-38.pyc
deleted file mode 100644
index 7853be6..0000000
Binary files a/myenv/Lib/site-packages/__pycache__/easy_install.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/__pycache__/socks.cpython-38.pyc b/myenv/Lib/site-packages/__pycache__/socks.cpython-38.pyc
deleted file mode 100644
index d2cd2b5..0000000
Binary files a/myenv/Lib/site-packages/__pycache__/socks.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/__pycache__/sockshandler.cpython-38.pyc b/myenv/Lib/site-packages/__pycache__/sockshandler.cpython-38.pyc
deleted file mode 100644
index 8af9f56..0000000
Binary files a/myenv/Lib/site-packages/__pycache__/sockshandler.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/__pycache__/typing_extensions.cpython-38.pyc b/myenv/Lib/site-packages/__pycache__/typing_extensions.cpython-38.pyc
deleted file mode 100644
index 151504a..0000000
Binary files a/myenv/Lib/site-packages/__pycache__/typing_extensions.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/_cffi_backend.cp38-win_amd64.pyd b/myenv/Lib/site-packages/_cffi_backend.cp38-win_amd64.pyd
deleted file mode 100644
index 91c3513..0000000
Binary files a/myenv/Lib/site-packages/_cffi_backend.cp38-win_amd64.pyd and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__init__.py b/myenv/Lib/site-packages/attr/__init__.py
deleted file mode 100644
index 5c6e065..0000000
--- a/myenv/Lib/site-packages/attr/__init__.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-"""
-Classes Without Boilerplate
-"""
-
-from functools import partial
-from typing import Callable, Literal, Protocol
-
-from . import converters, exceptions, filters, setters, validators
-from ._cmp import cmp_using
-from ._config import get_run_validators, set_run_validators
-from ._funcs import asdict, assoc, astuple, has, resolve_types
-from ._make import (
- NOTHING,
- Attribute,
- Converter,
- Factory,
- _Nothing,
- attrib,
- attrs,
- evolve,
- 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
-
-
-NothingType = Literal[_Nothing.NOTHING]
-
-__all__ = [
- "NOTHING",
- "Attribute",
- "AttrsInstance",
- "Converter",
- "Factory",
- "NothingType",
- "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:
- if name not in ("__version__", "__version_info__"):
- msg = f"module {mod_name} has no attribute {name}"
- raise AttributeError(msg)
-
- from importlib.metadata import metadata
-
- meta = metadata("attrs")
-
- if name == "__version_info__":
- return VersionInfo._from_version_string(meta["version"])
-
- return meta["version"]
-
- return __getattr__
-
-
-__getattr__ = _make_getattr(__name__)
diff --git a/myenv/Lib/site-packages/attr/__init__.pyi b/myenv/Lib/site-packages/attr/__init__.pyi
deleted file mode 100644
index 133e501..0000000
--- a/myenv/Lib/site-packages/attr/__init__.pyi
+++ /dev/null
@@ -1,389 +0,0 @@
-import enum
-import sys
-
-from typing import (
- Any,
- Callable,
- Generic,
- Literal,
- Mapping,
- Protocol,
- Sequence,
- TypeVar,
- 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
-from attrs import (
- define as define,
- field as field,
- mutable as mutable,
- frozen as frozen,
- _EqOrderType,
- _ValidatorType,
- _ConverterType,
- _ReprArgType,
- _OnSetAttrType,
- _OnSetAttrArgType,
- _FieldTransformer,
- _ValidatorArgType,
-)
-
-if sys.version_info >= (3, 10):
- from typing import TypeGuard, TypeAlias
-else:
- from typing_extensions import TypeGuard, TypeAlias
-
-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)
-
-_FilterType = Callable[["Attribute[_T]", _T], bool]
-
-# 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
-NothingType: TypeAlias = Literal[_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.
-
-@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: ...
-
-In = TypeVar("In")
-Out = TypeVar("Out")
-
-class Converter(Generic[In, Out]):
- @overload
- def __init__(self, converter: Callable[[In], Out]) -> None: ...
- @overload
- def __init__(
- self,
- converter: Callable[[In, AttrsInstance, Attribute], Out],
- *,
- takes_self: Literal[True],
- takes_field: Literal[True],
- ) -> None: ...
- @overload
- def __init__(
- self,
- converter: Callable[[In, Attribute], Out],
- *,
- takes_field: Literal[True],
- ) -> None: ...
- @overload
- def __init__(
- self,
- converter: Callable[[In, AttrsInstance], Out],
- *,
- takes_self: Literal[True],
- ) -> None: ...
-
-class Attribute(Generic[_T]):
- name: str
- default: _T | None
- validator: _ValidatorType[_T] | None
- repr: _ReprArgType
- cmp: _EqOrderType
- eq: _EqOrderType
- order: _EqOrderType
- hash: bool | None
- init: bool
- converter: Converter | None
- metadata: dict[Any, Any]
- type: type[_T] | None
- kw_only: bool
- on_setattr: _OnSetAttrType
- alias: str | None
-
- 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: _EqOrderType | None = ...,
- hash: bool | None = ...,
- init: bool = ...,
- metadata: Mapping[Any, Any] | None = ...,
- type: None = ...,
- converter: None = ...,
- factory: None = ...,
- kw_only: bool = ...,
- eq: _EqOrderType | None = ...,
- order: _EqOrderType | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- alias: str | None = ...,
-) -> Any: ...
-
-# This form catches an explicit None or no default and infers the type from the
-# other arguments.
-@overload
-def attrib(
- default: None = ...,
- validator: _ValidatorArgType[_T] | None = ...,
- repr: _ReprArgType = ...,
- cmp: _EqOrderType | None = ...,
- hash: bool | None = ...,
- init: bool = ...,
- metadata: Mapping[Any, Any] | None = ...,
- type: type[_T] | None = ...,
- converter: _ConverterType
- | list[_ConverterType]
- | tuple[_ConverterType]
- | None = ...,
- factory: Callable[[], _T] | None = ...,
- kw_only: bool = ...,
- eq: _EqOrderType | None = ...,
- order: _EqOrderType | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- alias: str | None = ...,
-) -> _T: ...
-
-# This form catches an explicit default argument.
-@overload
-def attrib(
- default: _T,
- validator: _ValidatorArgType[_T] | None = ...,
- repr: _ReprArgType = ...,
- cmp: _EqOrderType | None = ...,
- hash: bool | None = ...,
- init: bool = ...,
- metadata: Mapping[Any, Any] | None = ...,
- type: type[_T] | None = ...,
- converter: _ConverterType
- | list[_ConverterType]
- | tuple[_ConverterType]
- | None = ...,
- factory: Callable[[], _T] | None = ...,
- kw_only: bool = ...,
- eq: _EqOrderType | None = ...,
- order: _EqOrderType | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- alias: str | None = ...,
-) -> _T: ...
-
-# This form covers type=non-Type: e.g. forward references (str), Any
-@overload
-def attrib(
- default: _T | None = ...,
- validator: _ValidatorArgType[_T] | None = ...,
- repr: _ReprArgType = ...,
- cmp: _EqOrderType | None = ...,
- hash: bool | None = ...,
- init: bool = ...,
- metadata: Mapping[Any, Any] | None = ...,
- type: object = ...,
- converter: _ConverterType
- | list[_ConverterType]
- | tuple[_ConverterType]
- | None = ...,
- factory: Callable[[], _T] | None = ...,
- kw_only: bool = ...,
- eq: _EqOrderType | None = ...,
- order: _EqOrderType | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- alias: str | None = ...,
-) -> Any: ...
-@overload
-@dataclass_transform(order_default=True, field_specifiers=(attrib, field))
-def attrs(
- maybe_cls: _C,
- these: dict[str, Any] | None = ...,
- repr_ns: str | None = ...,
- repr: bool = ...,
- cmp: _EqOrderType | None = ...,
- hash: bool | None = ...,
- init: bool = ...,
- slots: bool = ...,
- frozen: bool = ...,
- weakref_slot: bool = ...,
- str: bool = ...,
- auto_attribs: bool = ...,
- kw_only: bool = ...,
- cache_hash: bool = ...,
- auto_exc: bool = ...,
- eq: _EqOrderType | None = ...,
- order: _EqOrderType | None = ...,
- auto_detect: bool = ...,
- collect_by_mro: bool = ...,
- getstate_setstate: bool | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- field_transformer: _FieldTransformer | None = ...,
- match_args: bool = ...,
- unsafe_hash: bool | None = ...,
-) -> _C: ...
-@overload
-@dataclass_transform(order_default=True, field_specifiers=(attrib, field))
-def attrs(
- maybe_cls: None = ...,
- these: dict[str, Any] | None = ...,
- repr_ns: str | None = ...,
- repr: bool = ...,
- cmp: _EqOrderType | None = ...,
- hash: bool | None = ...,
- init: bool = ...,
- slots: bool = ...,
- frozen: bool = ...,
- weakref_slot: bool = ...,
- str: bool = ...,
- auto_attribs: bool = ...,
- kw_only: bool = ...,
- cache_hash: bool = ...,
- auto_exc: bool = ...,
- eq: _EqOrderType | None = ...,
- order: _EqOrderType | None = ...,
- auto_detect: bool = ...,
- collect_by_mro: bool = ...,
- getstate_setstate: bool | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- field_transformer: _FieldTransformer | None = ...,
- match_args: bool = ...,
- unsafe_hash: bool | None = ...,
-) -> 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: dict[str, Any] | None = ...,
- localns: dict[str, Any] | None = ...,
- attribs: list[Attribute[Any]] | None = ...,
- 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: list[str] | tuple[str, ...] | dict[str, Any],
- bases: tuple[type, ...] = ...,
- class_body: dict[str, Any] | None = ...,
- repr_ns: str | None = ...,
- repr: bool = ...,
- cmp: _EqOrderType | None = ...,
- hash: bool | None = ...,
- init: bool = ...,
- slots: bool = ...,
- frozen: bool = ...,
- weakref_slot: bool = ...,
- str: bool = ...,
- auto_attribs: bool = ...,
- kw_only: bool = ...,
- cache_hash: bool = ...,
- auto_exc: bool = ...,
- eq: _EqOrderType | None = ...,
- order: _EqOrderType | None = ...,
- collect_by_mro: bool = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- field_transformer: _FieldTransformer | None = ...,
-) -> 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: _FilterType[Any] | None = ...,
- dict_factory: type[Mapping[Any, Any]] = ...,
- retain_collection_types: bool = ...,
- value_serializer: Callable[[type, Attribute[Any], Any], Any] | None = ...,
- tuple_keys: bool | None = ...,
-) -> dict[str, Any]: ...
-
-# TODO: add support for returning NamedTuple from the mypy plugin
-def astuple(
- inst: AttrsInstance,
- recurse: bool = ...,
- filter: _FilterType[Any] | None = ...,
- 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/myenv/Lib/site-packages/attr/__pycache__/__init__.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/__init__.cpython-38.pyc
deleted file mode 100644
index 285884c..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/__init__.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/_cmp.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/_cmp.cpython-38.pyc
deleted file mode 100644
index 2330a09..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/_cmp.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/_compat.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/_compat.cpython-38.pyc
deleted file mode 100644
index b6eec28..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/_compat.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/_config.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/_config.cpython-38.pyc
deleted file mode 100644
index f919f4c..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/_config.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/_funcs.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/_funcs.cpython-38.pyc
deleted file mode 100644
index 4b7a3e0..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/_funcs.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/_make.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/_make.cpython-38.pyc
deleted file mode 100644
index 4e82fd2..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/_make.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/_next_gen.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/_next_gen.cpython-38.pyc
deleted file mode 100644
index 9debfec..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/_next_gen.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/_version_info.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/_version_info.cpython-38.pyc
deleted file mode 100644
index f0ac891..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/_version_info.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/converters.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/converters.cpython-38.pyc
deleted file mode 100644
index fd7b32d..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/converters.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/exceptions.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/exceptions.cpython-38.pyc
deleted file mode 100644
index 5e17b34..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/exceptions.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/filters.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/filters.cpython-38.pyc
deleted file mode 100644
index d5610d4..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/filters.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/setters.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/setters.cpython-38.pyc
deleted file mode 100644
index f747916..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/setters.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/__pycache__/validators.cpython-38.pyc b/myenv/Lib/site-packages/attr/__pycache__/validators.cpython-38.pyc
deleted file mode 100644
index 308a98d..0000000
Binary files a/myenv/Lib/site-packages/attr/__pycache__/validators.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attr/_cmp.py b/myenv/Lib/site-packages/attr/_cmp.py
deleted file mode 100644
index 09bab49..0000000
--- a/myenv/Lib/site-packages/attr/_cmp.py
+++ /dev/null
@@ -1,160 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-
-import functools
-import types
-
-from ._make import __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.
-
- Args:
- eq (typing.Callable | None):
- Callable used to evaluate equality of two objects.
-
- lt (typing.Callable | None):
- Callable used to evaluate whether one object is less than another
- object.
-
- le (typing.Callable | None):
- Callable used to evaluate whether one object is less than or equal
- to another object.
-
- gt (typing.Callable | None):
- Callable used to evaluate whether one object is greater than
- another object.
-
- ge (typing.Callable | None):
- Callable used to evaluate whether one object is greater than or
- equal to another object.
-
- require_same_type (bool):
- When `True`, equality and ordering methods will return
- `NotImplemented` if objects are not of the same type.
-
- class_name (str | None): 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__"] = __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/myenv/Lib/site-packages/attr/_cmp.pyi b/myenv/Lib/site-packages/attr/_cmp.pyi
deleted file mode 100644
index cc7893b..0000000
--- a/myenv/Lib/site-packages/attr/_cmp.pyi
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import Any, Callable
-
-_CompareWithType = Callable[[Any, Any], bool]
-
-def cmp_using(
- eq: _CompareWithType | None = ...,
- lt: _CompareWithType | None = ...,
- le: _CompareWithType | None = ...,
- gt: _CompareWithType | None = ...,
- ge: _CompareWithType | None = ...,
- require_same_type: bool = ...,
- class_name: str = ...,
-) -> type: ...
diff --git a/myenv/Lib/site-packages/attr/_compat.py b/myenv/Lib/site-packages/attr/_compat.py
deleted file mode 100644
index 22fcd78..0000000
--- a/myenv/Lib/site-packages/attr/_compat.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# 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_9_PLUS = sys.version_info[:2] >= (3, 9)
-PY_3_10_PLUS = sys.version_info[:2] >= (3, 10)
-PY_3_11_PLUS = sys.version_info[:2] >= (3, 11)
-PY_3_12_PLUS = sys.version_info[:2] >= (3, 12)
-PY_3_13_PLUS = sys.version_info[:2] >= (3, 13)
-PY_3_14_PLUS = sys.version_info[:2] >= (3, 14)
-
-
-if PY_3_14_PLUS: # pragma: no cover
- import annotationlib
-
- _get_annotations = annotationlib.get_annotations
-
-else:
-
- def _get_annotations(cls):
- """
- Get annotations for *cls*.
- """
- return cls.__dict__.get("__annotations__", {})
-
-
-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/myenv/Lib/site-packages/attr/_config.py b/myenv/Lib/site-packages/attr/_config.py
deleted file mode 100644
index 4b25772..0000000
--- a/myenv/Lib/site-packages/attr/_config.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-__all__ = ["get_run_validators", "set_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/myenv/Lib/site-packages/attr/_funcs.py b/myenv/Lib/site-packages/attr/_funcs.py
deleted file mode 100644
index c39fb8a..0000000
--- a/myenv/Lib/site-packages/attr/_funcs.py
+++ /dev/null
@@ -1,468 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-
-import copy
-
-from ._compat import PY_3_9_PLUS, get_generic_base
-from ._make import _OBJ_SETATTR, NOTHING, 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.
-
- Args:
- inst: Instance of an *attrs*-decorated class.
-
- recurse (bool): Recurse into classes that are also *attrs*-decorated.
-
- filter (~typing.Callable):
- 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.
-
- dict_factory (~typing.Callable):
- A callable to produce dictionaries from. For example, to produce
- ordered dictionaries instead of normal Python dictionaries, pass in
- ``collections.OrderedDict``.
-
- retain_collection_types (bool):
- Do not convert to `list` when encountering an attribute whose type
- is `tuple` or `set`. Only meaningful if *recurse* is `True`.
-
- value_serializer (typing.Callable | None):
- 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.
-
- Returns:
- Return type of *dict_factory*.
-
- Raises:
- 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.
-
- Args:
- inst: Instance of an *attrs*-decorated class.
-
- recurse (bool):
- Recurse into classes that are also *attrs*-decorated.
-
- filter (~typing.Callable):
- 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.
-
- tuple_factory (~typing.Callable):
- A callable to produce tuples from. For example, to produce lists
- instead of tuples.
-
- retain_collection_types (bool):
- Do not convert to `list` or `dict` when encountering an attribute
- which type is `tuple`, `dict` or `set`. Only meaningful if
- *recurse* is `True`.
-
- Returns:
- Return type of *tuple_factory*
-
- Raises:
- 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.
-
- Args:
- cls (type): Class to introspect.
-
- Raises:
- TypeError: If *cls* is not a class.
-
- Returns:
- 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
-
- Args:
- inst: Instance of a class with *attrs* attributes.
-
- changes: Keyword changes in the new copy.
-
- Returns:
- A copy of inst with *changes* incorporated.
-
- Raises:
- attrs.exceptions.AttrsAttributeNotFoundError:
- If *attr_name* couldn't be found on *cls*.
-
- 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`, though.
- """
- 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 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 :class:`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, for example, 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.
-
- Args:
- cls (type): Class to resolve.
-
- globalns (dict | None): Dictionary containing global variables.
-
- localns (dict | None): Dictionary containing local variables.
-
- attribs (list | None):
- 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.
-
- include_extras (bool):
- 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.
-
- Raises:
- TypeError: If *cls* is not a class.
-
- attrs.exceptions.NotAnAttrsClassError:
- If *cls* is not an *attrs* class and you didn't pass any attribs.
-
- 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/myenv/Lib/site-packages/attr/_make.py b/myenv/Lib/site-packages/attr/_make.py
deleted file mode 100644
index e84d979..0000000
--- a/myenv/Lib/site-packages/attr/_make.py
+++ /dev/null
@@ -1,3123 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-from __future__ import annotations
-
-import abc
-import contextlib
-import copy
-import enum
-import inspect
-import itertools
-import linecache
-import sys
-import types
-import unicodedata
-
-from collections.abc import Callable, Mapping
-from functools import cached_property
-from typing import Any, NamedTuple, TypeVar
-
-# 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 (
- PY_3_10_PLUS,
- PY_3_11_PLUS,
- PY_3_13_PLUS,
- _AnnotationExtractor,
- _get_annotations,
- 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_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()
-
-_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.
-
-When using in 3rd party code, use `attrs.NothingType` for type annotations.
-"""
-
-
-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 field / attribute on a class.
-
- Identical to `attrs.field`, except it's not keyword-only.
-
- Consider using `attrs.field` in new code (``attr.ib`` will *never* go away,
- though).
-
- .. warning::
-
- Does **nothing** unless the class is also decorated with
- `attr.s` (or similar)!
-
-
- .. 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: str,
- globs: dict[str, Any] | None,
- locs: Mapping[str, object] | None = None,
- filename: str = "",
-) -> None:
- """
- Evaluate the script with the given global (globs) and local (locs)
- variables.
- """
- bytecode = compile(script, filename, "exec")
- eval(bytecode, globs, locs)
-
-
-def _linecache_and_compile(
- script: str,
- filename: str,
- globs: dict[str, Any] | None,
- locals: Mapping[str, object] | None = None,
-) -> dict[str, Any]:
- """
- Cache the script with _linecache_, compile it and return the _locals_.
- """
-
- locs = {} if locals is None else locals
-
- # 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
-
-
-def _make_attr_tuple_class(cls_name: str, attr_names: list[str]) -> type:
- """
- 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"
- body = {}
- for i, attr_name in enumerate(attr_names):
-
- def getter(self, i=i):
- return self[i]
-
- body[attr_name] = property(getter)
- return type(attr_class_name, (tuple,), body)
-
-
-# Tuple class for extracted attributes from a class definition.
-# `base_attrs` is a subset of `attrs`.
-class _Attributes(NamedTuple):
- attrs: type
- base_attrs: list[Attribute]
- base_attrs_map: dict[str, type]
-
-
-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).
- """
- return attrib_name in cls.__dict__
-
-
-def _collect_base_attrs(
- cls, taken_attr_names
-) -> tuple[list[Attribute], dict[str, type]]:
- """
- 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
-) -> _Attributes:
- """
- 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.
-
- If *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 attr.__class__ is _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 a.__class__ is not _CountingAttr:
- a = attrib(a)
- ca_list.append((attr_name, a))
-
- unannotated = ca_names - annot_names
- if unannotated:
- 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 attr.__class__ is _CountingAttr
- ),
- key=lambda e: e[1].counter,
- )
-
- fca = Attribute.from_counting_attr
- own_attrs = [
- fca(attr_name, ca, 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
-
- if field_transformer is not None:
- attrs = tuple(field_transformer(cls, attrs))
-
- # Check attr order after executing the field_transformer.
- # 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
-
- # Resolve default field alias after executing field_transformer.
- # This allows field_transformer to differentiate between explicit vs
- # default aliases and supply their own defaults.
- for a in attrs:
- if not a.alias:
- # Evolve is very slow, so we hold our nose and do it dirty.
- _OBJ_SETATTR.__get__(a)("alias", _default_init_alias_for(a.name))
-
- # 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(_cls):",
- " __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(
- [
- " try:",
- " return super().__getattribute__(item)",
- " except AttributeError:",
- " if not hasattr(super(), '__getattr__'):",
- " raise",
- " 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(_cls)",
- ]
- )
-
- unique_filename = _generate_unique_filename(cls, "getattr")
-
- glob = {
- "cached_properties": cached_properties,
- "_cached_setattr_get": _OBJ_SETATTR.__get__,
- "original_getattr": original_getattr,
- }
-
- return _linecache_and_compile(
- "\n".join(lines), unique_filename, glob, locals={"_cls": cls}
- )["__getattr__"]
-
-
-def _frozen_setattrs(self, name, value):
- """
- Attached to frozen classes as __setattr__.
- """
- if isinstance(self, BaseException) and name in (
- "__cause__",
- "__context__",
- "__traceback__",
- "__suppress_context__",
- "__notes__",
- ):
- BaseException.__setattr__(self, name, value)
- return
-
- raise FrozenInstanceError
-
-
-def _frozen_delattrs(self, name):
- """
- Attached to frozen classes as __delattr__.
- """
- if isinstance(self, BaseException) and name in ("__notes__",):
- BaseException.__delattr__(self, name)
- return
-
- raise FrozenInstanceError
-
-
-def evolve(*args, **changes):
- """
- Create a new instance, based on the first positional argument with
- *changes* applied.
-
- .. tip::
-
- On Python 3.13 and later, you can also use `copy.replace` instead.
-
- Args:
-
- inst:
- Instance of a class with *attrs* attributes. *inst* must be passed
- as a positional argument.
-
- changes:
- Keyword changes in the new copy.
-
- Returns:
- A copy of inst with *changes* incorporated.
-
- Raises:
- TypeError:
- If *attr_name* couldn't be found in the class ``__init__``.
-
- 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.
- .. versionchanged:: 24.1.0
- *inst* can't be passed as a keyword argument anymore.
- """
- try:
- (inst,) = args
- except ValueError:
- msg = (
- f"evolve() takes 1 positional argument, but {len(args)} were given"
- )
- raise TypeError(msg) from None
-
- 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)
-
-
-class _ClassBuilder:
- """
- Iteratively build *one* class.
- """
-
- __slots__ = (
- "_add_method_dunders",
- "_attr_names",
- "_attrs",
- "_base_attr_map",
- "_base_names",
- "_cache_hash",
- "_cls",
- "_cls_dict",
- "_delete_attribs",
- "_frozen",
- "_has_custom_setattr",
- "_has_post_init",
- "_has_pre_init",
- "_is_exc",
- "_on_setattr",
- "_pre_init_has_args",
- "_repr_added",
- "_script_snippets",
- "_slots",
- "_weakref_slot",
- "_wrote_own_setattr",
- )
-
- def __init__(
- self,
- cls: type,
- 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 (
- _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 == _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()
-
- # tuples of script, globs, hook
- self._script_snippets: list[
- tuple[str, dict, Callable[[dict, dict], Any]]
- ] = []
- self._repr_added = False
-
- # We want to only do this check once; in 99.9% of cases these
- # exist.
- if not hasattr(self._cls, "__module__") or not hasattr(
- self._cls, "__qualname__"
- ):
- self._add_method_dunders = self._add_method_dunders_safe
- else:
- self._add_method_dunders = self._add_method_dunders_unsafe
-
- def __repr__(self):
- return f"<_ClassBuilder(cls={self._cls.__name__})>"
-
- def _eval_snippets(self) -> None:
- """
- Evaluate any registered snippets in one go.
- """
- script = "\n".join([snippet[0] for snippet in self._script_snippets])
- globs = {}
- for _, snippet_globs, _ in self._script_snippets:
- globs.update(snippet_globs)
-
- locs = _linecache_and_compile(
- script,
- _generate_unique_filename(self._cls, "methods"),
- globs,
- )
-
- for _, _, hook in self._script_snippets:
- hook(self._cls_dict, locs)
-
- def build_class(self):
- """
- Finalize class based on the accumulated configuration.
-
- Builder cannot be used after calling this method.
- """
- self._eval_snippets()
- if self._slots is True:
- cls = self._create_slots_class()
- else:
- cls = self._patch_original_class()
- if PY_3_10_PLUS:
- cls = abc.update_abstractmethods(cls)
-
- # The method gets only called if it's not inherited from a base class.
- # _has_own_attribute does NOT work properly for classmethods.
- if (
- getattr(cls, "__attrs_init_subclass__", None)
- and "__attrs_init_subclass__" not in cls.__dict__
- ):
- cls.__attrs_init_subclass__()
-
- return cls
-
- 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__",)
-
- cached_properties = {
- name: cached_prop.func
- for name, cached_prop in cd.items()
- if isinstance(cached_prop, cached_property)
- }
-
- # 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:
- class_annotations = _get_annotations(self._cls)
- for name, func in cached_properties.items():
- # Add cached properties to names for slotting.
- names += (name,)
- # Clear out function from class to avoid clashing.
- del cd[name]
- additional_closure_functions_to_update.append(func)
- 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):
- script, globs = _make_repr_script(self._attrs, ns)
-
- def _attach_repr(cls_dict, globs):
- cls_dict["__repr__"] = self._add_method_dunders(globs["__repr__"])
-
- self._script_snippets.append((script, globs, _attach_repr))
- self._repr_added = True
- return self
-
- def add_str(self):
- if not self._repr_added:
- 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):
- script, globs = _make_hash_script(
- self._cls,
- self._attrs,
- frozen=self._frozen,
- cache_hash=self._cache_hash,
- )
-
- def attach_hash(cls_dict: dict, locs: dict) -> None:
- cls_dict["__hash__"] = self._add_method_dunders(locs["__hash__"])
-
- self._script_snippets.append((script, globs, attach_hash))
-
- return self
-
- def add_init(self):
- script, globs, annotations = _make_init_script(
- 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,
- )
-
- def _attach_init(cls_dict, globs):
- init = globs["__init__"]
- init.__annotations__ = annotations
- cls_dict["__init__"] = self._add_method_dunders(init)
-
- self._script_snippets.append((script, globs, _attach_init))
-
- return self
-
- def add_replace(self):
- self._cls_dict["__replace__"] = self._add_method_dunders(
- lambda self, **changes: evolve(self, **changes)
- )
- 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):
- script, globs, annotations = _make_init_script(
- 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,
- )
-
- def _attach_attrs_init(cls_dict, globs):
- init = globs["__attrs_init__"]
- init.__annotations__ = annotations
- cls_dict["__attrs_init__"] = self._add_method_dunders(init)
-
- self._script_snippets.append((script, globs, _attach_attrs_init))
-
- return self
-
- def add_eq(self):
- cd = self._cls_dict
-
- script, globs = _make_eq_script(self._attrs)
-
- def _attach_eq(cls_dict, globs):
- cls_dict["__eq__"] = self._add_method_dunders(globs["__eq__"])
-
- self._script_snippets.append((script, globs, _attach_eq))
-
- cd["__ne__"] = __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):
- 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_unsafe(self, method: Callable) -> Callable:
- """
- Add __module__ and __qualname__ to a *method*.
- """
- method.__module__ = self._cls.__module__
-
- method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}"
-
- method.__doc__ = (
- f"Method generated by attrs for class {self._cls.__qualname__}."
- )
-
- return method
-
- def _add_method_dunders_safe(self, method: Callable) -> Callable:
- """
- 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__ = f"Method generated by attrs for class {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.
-
- Consider using `attrs.define` / `attrs.frozen` in new code (``attr.s`` will
- *never* go away, though).
-
- Args:
- repr_ns (str):
- When using nested classes, there was no way in Python 2 to
- automatically detect that. This argument allows to set a custom
- name for a more meaningful ``repr`` output. This argument is
- pointless in Python 3 and is therefore deprecated.
-
- .. caution::
- Refer to `attrs.define` for the rest of the parameters, but note that they
- can have different defaults.
-
- Notably, leaving *on_setattr* as `None` will **not** add any hooks.
-
- .. 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).
- .. deprecated:: 24.1.0 *repr_ns*
- .. versionchanged:: 24.1.0
- Instances are not compared as tuples of attributes anymore, but using a
- big ``and`` condition. This is faster and has more correct behavior for
- uncomparable values like `math.nan`.
- .. versionadded:: 24.1.0
- If a class has an *inherited* classmethod called
- ``__attrs_init_subclass__``, it is executed after the class is created.
- .. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*.
- """
- if repr_ns is not None:
- import warnings
-
- warnings.warn(
- DeprecationWarning(
- "The `repr_ns` argument is deprecated and will be removed in or after August 2025."
- ),
- stacklevel=2,
- )
-
- 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()
-
- if not frozen:
- 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 PY_3_13_PLUS and not _has_own_attribute(cls, "__replace__"):
- builder.add_replace()
-
- if (
- PY_3_10_PLUS
- 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: type, func_name: str) -> str:
- """
- Create a "filename" suitable for a function being generated.
- """
- return (
- f""
- )
-
-
-def _make_hash_script(
- cls: type, attrs: list[Attribute], frozen: bool, cache_hash: bool
-) -> tuple[str, dict]:
- attrs = tuple(
- a for a in attrs if a.hash is True or (a.hash is None and a.eq is True)
- )
-
- tab = " "
-
- type_hash = hash(_generate_unique_filename(cls, "hash"))
- # 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 script, globs
-
-
-def _add_hash(cls: type, attrs: list[Attribute]):
- """
- Add a hash method to *cls*.
- """
- script, globs = _make_hash_script(
- cls, attrs, frozen=False, cache_hash=False
- )
- _compile_and_eval(
- script, globs, filename=_generate_unique_filename(cls, "__hash__")
- )
- cls.__hash__ = globs["__hash__"]
- return cls
-
-
-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
-
-
-def _make_eq_script(attrs: list) -> tuple[str, dict]:
- """
- Create __eq__ method for *cls* with *attrs*.
- """
- attrs = [a for a in attrs if a.eq]
-
- lines = [
- "def __eq__(self, other):",
- " if other.__class__ is not self.__class__:",
- " return NotImplemented",
- ]
-
- globs = {}
- if attrs:
- lines.append(" return (")
- 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}) == {cmp_name}(other.{a.name})"
- )
- else:
- lines.append(f" self.{a.name} == other.{a.name}")
- if a is not attrs[-1]:
- lines[-1] = f"{lines[-1]} and"
- lines.append(" )")
- else:
- lines.append(" return True")
-
- script = "\n".join(lines)
-
- return script, 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__
-
- script, globs = _make_eq_script(attrs)
- _compile_and_eval(
- script, globs, filename=_generate_unique_filename(cls, "__eq__")
- )
- cls.__eq__ = globs["__eq__"]
- cls.__ne__ = __ne__
-
- return cls
-
-
-def _make_repr_script(attrs, ns) -> tuple[str, dict]:
- """
- Create the source and globs for a __repr__ and return it.
- """
- # 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 "\n".join(lines), globs
-
-
-def _add_repr(cls, ns=None, attrs=None):
- """
- Add a repr method to *cls*.
- """
- if attrs is None:
- attrs = cls.__attrs_attrs__
-
- script, globs = _make_repr_script(attrs, ns)
- _compile_and_eval(
- script, globs, filename=_generate_unique_filename(cls, "__repr__")
- )
- cls.__repr__ = globs["__repr__"]
- 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).
-
- Args:
- cls (type): Class to introspect.
-
- Raises:
- TypeError: If *cls* is not a class.
-
- attrs.exceptions.NotAnAttrsClassError:
- If *cls* is not an *attrs* class.
-
- Returns:
- 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.
-
- Args:
- cls (type): Class to introspect.
-
- Raises:
- TypeError: If *cls* is not a class.
-
- attrs.exceptions.NotAnAttrsClassError:
- If *cls* is not an *attrs* class.
-
- Returns:
- dict[str, attrs.Attribute]: Dict of attribute name to definition
-
- .. 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.
-
- Args:
- 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_attr(a_name, base_attr_map):
- """
- Check if the attribute name comes from a slot class.
- """
- cls = base_attr_map.get(a_name)
- return cls and "__slots__" in cls.__dict__
-
-
-def _make_init_script(
- cls,
- attrs,
- pre_init,
- pre_init_has_args,
- post_init,
- frozen,
- slots,
- cache_hash,
- base_attr_map,
- is_exc,
- cls_on_setattr,
- attrs_init,
-) -> tuple[str, dict, dict]:
- 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
-
- 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 attrs_init else "__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__
-
- return script, globs, annotations
-
-
-def _setattr(attr_name: str, value_var: str, has_on_setattr: bool) -> str:
- """
- 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: str, value_var: str, has_on_setattr: bool, converter: Converter
-) -> str:
- """
- Use the cached object.setattr to set *attr_name* to *value_var*, but run
- its converter first.
- """
- return f"_setattr('{attr_name}', {converter._fmt_converter_call(attr_name, value_var)})"
-
-
-def _assign(attr_name: str, value: str, has_on_setattr: bool) -> str:
- """
- 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: str, value_var: str, has_on_setattr: bool, converter: Converter
-) -> str:
- """
- 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, converter)
-
- return f"self.{attr_name} = {converter._fmt_converter_call(attr_name, value_var)}"
-
-
-def _determine_setters(
- frozen: bool, slots: bool, base_attr_map: dict[str, type]
-):
- """
- Determine the correct setter functions based on whether a class is frozen
- and/or slotted.
- """
- if frozen is True:
- if slots is True:
- return (), _setattr, _setattr_with_converter
-
- # 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
-
- def fmt_setter(
- attr_name: str, value_var: str, has_on_setattr: bool
- ) -> str:
- 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: str,
- value_var: str,
- has_on_setattr: bool,
- converter: Converter,
- ) -> str:
- if has_on_setattr or _is_slot_attr(attr_name, base_attr_map):
- return _setattr_with_converter(
- attr_name, value_var, has_on_setattr, converter
- )
-
- return f"_inst_dict['{attr_name}'] = {converter._fmt_converter_call(attr_name, value_var)}"
-
- return (
- ("_inst_dict = self.__dict__",),
- fmt_setter,
- fmt_setter_with_converter,
- )
-
- # Not frozen -- we can just assign directly.
- return (), _assign, _assign_with_converter
-
-
-def _attrs_to_init_script(
- attrs: list[Attribute],
- is_frozen: bool,
- is_slotted: bool,
- call_pre_init: bool,
- pre_init_has_args: bool,
- call_post_init: bool,
- does_cache_hash: bool,
- base_attr_map: dict[str, type],
- is_exc: bool,
- needs_cached_setattr: bool,
- has_cls_on_setattr: bool,
- method_name: str,
-) -> tuple[str, dict, dict]:
- """
- Return a script of an initializer for *attrs*, a dict of globals, and
- annotations for the initializer.
-
- The globals are required by the generated script.
- """
- lines = ["self.__attrs_pre_init__()"] if call_pre_init else []
-
- if needs_cached_setattr:
- lines.append(
- # Circumvent the __setattr__ descriptor to save one lookup per
- # assignment. Note _setattr will be used again below if
- # does_cache_hash is True.
- "_setattr = _cached_setattr_get(self)"
- )
-
- extra_lines, fmt_setter, fmt_setter_with_converter = _determine_setters(
- is_frozen, is_slotted, base_attr_map
- )
- lines.extend(extra_lines)
-
- 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.converter is not None and not isinstance(a.converter, Converter):
- converter = Converter(a.converter)
- else:
- converter = a.converter
-
- if a.init is False:
- if has_factory:
- init_factory_name = _INIT_FACTORY_PAT % (a.name,)
- if converter is not None:
- lines.append(
- fmt_setter_with_converter(
- attr_name,
- init_factory_name + f"({maybe_self})",
- has_on_setattr,
- converter,
- )
- )
- names_for_globals[converter._get_global_name(a.name)] = (
- converter.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 converter is not None:
- lines.append(
- fmt_setter_with_converter(
- attr_name,
- f"attr_dict['{attr_name}'].default",
- has_on_setattr,
- converter,
- )
- )
- names_for_globals[converter._get_global_name(a.name)] = (
- converter.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 converter is not None:
- lines.append(
- fmt_setter_with_converter(
- attr_name, arg_name, has_on_setattr, converter
- )
- )
- names_for_globals[converter._get_global_name(a.name)] = (
- converter.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 converter is not None:
- lines.append(
- " "
- + fmt_setter_with_converter(
- attr_name, arg_name, has_on_setattr, converter
- )
- )
- lines.append("else:")
- lines.append(
- " "
- + fmt_setter_with_converter(
- attr_name,
- init_factory_name + "(" + maybe_self + ")",
- has_on_setattr,
- converter,
- )
- )
- names_for_globals[converter._get_global_name(a.name)] = (
- converter.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 converter is not None:
- lines.append(
- fmt_setter_with_converter(
- attr_name, arg_name, has_on_setattr, converter
- )
- )
- names_for_globals[converter._get_global_name(a.name)] = (
- converter.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 converter is None:
- annotations[arg_name] = a.type
- elif converter is not None and converter._first_param_type:
- # Use the type from the converter if present.
- annotations[arg_name] = converter._first_param_type
-
- 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 call_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 does_cache_hash:
- if is_frozen:
- if is_slotted:
- init_hash_cache = f"_setattr('{_HASH_CACHE_FIELD}', None)"
- else:
- init_hash_cache = f"_inst_dict['{_HASH_CACHE_FIELD}'] = None"
- else:
- init_hash_cache = f"self.{_HASH_CACHE_FIELD} = None"
- lines.append(init_hash_cache)
-
- # 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:
- # leading comma & kw_only args
- args += f"{', ' if args else ''}*, {', '.join(kw_only_args)}"
- pre_init_kw_only_args = ", ".join(
- [
- f"{kw_arg_name}={kw_arg_name}"
- # We need to remove the defaults from the kw_only_args.
- for kw_arg_name in (kwa.split("=")[0] for kwa in kw_only_args)
- ]
- )
- pre_init_args += ", " if pre_init_args else ""
- pre_init_args += pre_init_kw_only_args
-
- if call_pre_init and pre_init_has_args:
- # If pre init method has arguments, pass same arguments as `__init__`.
- lines[0] = f"self.__attrs_pre_init__({pre_init_args})"
-
- # Python <3.12 doesn't allow backslashes in f-strings.
- NL = "\n "
- return (
- f"""def {method_name}(self, {args}):
- {NL.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`.
- """
-
- # These slots must NOT be reordered because we use them later for
- # instantiation.
- __slots__ = ( # noqa: RUF023
- "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: str, ca: _CountingAttr, type=None):
- # type holds the annotated value. deal with conflicts:
- if type is None:
- type = ca.type
- elif ca.type is not None:
- msg = f"Type annotation and type argument cannot both be present for '{name}'."
- raise ValueError(msg)
- return cls(
- name,
- ca._default,
- ca._validator,
- ca.repr,
- None,
- ca.hash,
- ca.init,
- False,
- ca.metadata,
- type,
- ca.converter,
- ca.kw_only,
- ca.eq,
- ca.eq_key,
- ca.order,
- ca.order_key,
- ca.on_setattr,
- ca.alias,
- )
-
- # 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 :class:`attrs.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__ = (
- "_default",
- "_validator",
- "alias",
- "converter",
- "counter",
- "eq",
- "eq_key",
- "hash",
- "init",
- "kw_only",
- "metadata",
- "on_setattr",
- "order",
- "order_key",
- "repr",
- "type",
- )
- __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.
-
- Args:
- factory (typing.Callable):
- A callable that takes either none or exactly one mandatory
- positional argument depending on *takes_self*.
-
- takes_self (bool):
- 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)
-
-
-class Converter:
- """
- Stores a converter callable.
-
- Allows for the wrapped converter to take additional arguments. The
- arguments are passed in the order they are documented.
-
- Args:
- converter (Callable): A callable that converts the passed value.
-
- takes_self (bool):
- Pass the partially initialized instance that is being initialized
- as a positional argument. (default: `False`)
-
- takes_field (bool):
- Pass the field definition (an :class:`Attribute`) into the
- converter as a positional argument. (default: `False`)
-
- .. versionadded:: 24.1.0
- """
-
- __slots__ = (
- "__call__",
- "_first_param_type",
- "_global_name",
- "converter",
- "takes_field",
- "takes_self",
- )
-
- def __init__(self, converter, *, takes_self=False, takes_field=False):
- self.converter = converter
- self.takes_self = takes_self
- self.takes_field = takes_field
-
- ex = _AnnotationExtractor(converter)
- self._first_param_type = ex.get_first_param_type()
-
- if not (self.takes_self or self.takes_field):
- self.__call__ = lambda value, _, __: self.converter(value)
- elif self.takes_self and not self.takes_field:
- self.__call__ = lambda value, instance, __: self.converter(
- value, instance
- )
- elif not self.takes_self and self.takes_field:
- self.__call__ = lambda value, __, field: self.converter(
- value, field
- )
- else:
- self.__call__ = lambda value, instance, field: self.converter(
- value, instance, field
- )
-
- rt = ex.get_return_type()
- if rt is not None:
- self.__call__.__annotations__["return"] = rt
-
- @staticmethod
- def _get_global_name(attr_name: str) -> str:
- """
- Return the name that a converter for an attribute name *attr_name*
- would have.
- """
- return f"__attr_converter_{attr_name}"
-
- def _fmt_converter_call(self, attr_name: str, value_var: str) -> str:
- """
- Return a string that calls the converter for an attribute name
- *attr_name* and the value in variable named *value_var* according to
- `self.takes_self` and `self.takes_field`.
- """
- if not (self.takes_self or self.takes_field):
- return f"{self._get_global_name(attr_name)}({value_var})"
-
- if self.takes_self and self.takes_field:
- return f"{self._get_global_name(attr_name)}({value_var}, self, attr_dict['{attr_name}'])"
-
- if self.takes_self:
- return f"{self._get_global_name(attr_name)}({value_var}, self)"
-
- return f"{self._get_global_name(attr_name)}({value_var}, attr_dict['{attr_name}'])"
-
- def __getstate__(self):
- """
- Return a dict containing only converter and takes_self -- the rest gets
- computed when loading.
- """
- return {
- "converter": self.converter,
- "takes_self": self.takes_self,
- "takes_field": self.takes_field,
- }
-
- def __setstate__(self, state):
- """
- Load instance from state.
- """
- self.__init__(**state)
-
-
-_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 ("converter", "takes_self", "takes_field")
-]
-
-Converter = _add_hash(
- _add_eq(_add_repr(Converter, 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*.
-
- .. note::
-
- ``make_class()`` is a thin wrapper around `attr.s`, not `attrs.define`
- which means that it doesn't come with some of the improved defaults.
-
- For example, if you want the same ``on_setattr`` behavior as in
- `attrs.define`, you have to pass the hooks yourself: ``make_class(...,
- on_setattr=setters.pipe(setters.convert, setters.validate)``
-
- .. warning::
-
- It is *your* duty to ensure that the class name and the attribute names
- are valid identifiers. ``make_class()`` will *not* validate them for
- you.
-
- Args:
- name (str): The name for the new class.
-
- attrs (list | dict):
- 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.
-
- bases (tuple[type, ...]): Classes that the new class will subclass.
-
- class_body (dict):
- An optional dictionary of class attributes for the new class.
-
- attributes_arguments: Passed unmodified to `attr.s`.
-
- Returns:
- type: A new class with *attrs*.
-
- .. versionadded:: 17.1.0 *bases*
- .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.
- .. versionchanged:: 23.2.0 *class_body*
- .. versionchanged:: 25.2.0 Class names can now be unicode.
- """
- # Class identifiers are converted into the normal form NFKC while parsing
- name = unicodedata.normalize("NFKC", name)
-
- 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,
- )
-
- cls = _attrs(these=cls_dict, **attributes_arguments)(type_)
- # Only add type annotations now or "_attrs()" will complain:
- cls.__annotations__ = {
- k: v.type for k, v in cls_dict.items() if v.type is not None
- }
- return cls
-
-
-# These are required by within this module so we define them here and merely
-# import into .validators / .converters.
-
-
-@attrs(slots=True, unsafe_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.
-
- Args:
- validators (~collections.abc.Iterable[typing.Callable]):
- 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.
-
- converters (~collections.abc.Iterable[typing.Callable]):
- Arbitrary number of converters.
-
- .. versionadded:: 20.1.0
- """
-
- return_instance = any(isinstance(c, Converter) for c in converters)
-
- if return_instance:
-
- def pipe_converter(val, inst, field):
- for c in converters:
- val = (
- c(val, inst, field) if isinstance(c, Converter) else c(val)
- )
-
- return val
-
- else:
-
- def pipe_converter(val):
- for c in converters:
- val = c(val)
-
- return val
-
- if not converters:
- # If the converter list is empty, pipe_converter is the identity.
- A = TypeVar("A")
- pipe_converter.__annotations__.update({"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
-
- last = converters[-1]
- if not PY_3_11_PLUS and isinstance(last, Converter):
- last = last.__call__
-
- # Get return type from last converter.
- rt = _AnnotationExtractor(last).get_return_type()
- if rt:
- pipe_converter.__annotations__["return"] = rt
-
- if return_instance:
- return Converter(pipe_converter, takes_self=True, takes_field=True)
- return pipe_converter
diff --git a/myenv/Lib/site-packages/attr/_next_gen.py b/myenv/Lib/site-packages/attr/_next_gen.py
deleted file mode 100644
index 9290664..0000000
--- a/myenv/Lib/site-packages/attr/_next_gen.py
+++ /dev/null
@@ -1,623 +0,0 @@
-# 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 (
- _DEFAULT_ON_SETATTR,
- NOTHING,
- _frozen_setattrs,
- 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"""
- A class decorator that adds :term:`dunder methods` according to
- :term:`fields ` specified using :doc:`type annotations `,
- `field()` calls, or the *these* argument.
-
- Since *attrs* patches or replaces an existing class, you cannot use
- `object.__init_subclass__` with *attrs* classes, because it runs too early.
- As a replacement, you can define ``__attrs_init_subclass__`` on your class.
- It will be called by *attrs* classes that subclass it after they're
- created. See also :ref:`init-subclass`.
-
- Args:
- slots (bool):
- 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 `.
-
- auto_detect (bool):
- Instead of setting the *init*, *repr*, *eq*, 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 (meaning, 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).
-
- Passing True or False` to *init*, *repr*, *eq*, or *hash*
- overrides whatever *auto_detect* would determine.
-
- auto_exc (bool):
- If the class subclasses `BaseException` (which implicitly includes
- any subclass of any exception), the following happens to behave
- like a well-behaved Python exception class:
-
- - the values for *eq*, *order*, and *hash* are ignored and the
- instances compare and hash by the instance's ids [#]_ ,
- - 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.
-
- .. [#]
- Note that *attrs* will *not* remove existing implementations of
- ``__hash__`` or the equality methods. It just won't add own
- ones.
-
- on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]):
- 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`.
-
- If left None, the default behavior is to run converters and
- validators whenever an attribute is set.
-
- init (bool):
- Create a ``__init__`` method that initializes the *attrs*
- attributes. Leading underscores are stripped for the argument name,
- unless an alias is set on the attribute.
-
- .. seealso::
- `init` shows advanced ways to customize the generated
- ``__init__`` method, including executing code before and after.
-
- repr(bool):
- Create a ``__repr__`` method with a human readable representation
- of *attrs* attributes.
-
- str (bool):
- Create a ``__str__`` method that is identical to ``__repr__``. This
- is usually not necessary except for `Exception`\ s.
-
- eq (bool | None):
- If True or None (default), add ``__eq__`` and ``__ne__`` methods
- that check two instances for equality.
-
- .. seealso::
- `comparison` describes how to customize the comparison behavior
- going as far comparing NumPy arrays.
-
- order (bool | None):
- If True, add ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__``
- methods that behave like *eq* above and allow instances to be
- ordered.
-
- They compare the instances as if they were tuples of their *attrs*
- attributes if and only if the types of both classes are
- *identical*.
-
- If `None` mirror value of *eq*.
-
- .. seealso:: `comparison`
-
- unsafe_hash (bool | None):
- 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 the
- 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 (for example, 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.
-
- hash (bool | None):
- Deprecated alias for *unsafe_hash*. *unsafe_hash* takes precedence.
-
- cache_hash (bool):
- 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.
-
- frozen (bool):
- 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.
-
- kw_only (bool):
- Make all attributes keyword-only in the generated ``__init__`` (if
- *init* is False, this parameter is ignored).
-
- weakref_slot (bool):
- Make instances weak-referenceable. This has no effect unless
- *slots* is True.
-
- field_transformer (~typing.Callable | None):
- A function that is called with the original class object and all
- fields right before *attrs* finalizes the class. You can use this,
- for example, to automatically add converters or validators to
- fields based on their types.
-
- .. seealso:: `transform-fields`
-
- match_args (bool):
- 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.
-
- collect_by_mro (bool):
- If True, *attrs* collects attributes from base classes correctly
- according to the `method resolution order
- `_. If False, *attrs*
- will mimic the (wrong) behavior of `dataclasses` and :pep:`681`.
-
- See also `issue #428
- `_.
-
- getstate_setstate (bool | None):
- .. 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 (meaning: not inherited), it is set to False
- (this is usually what you want).
-
- auto_attribs (bool | None):
- If True, look at type annotations to determine which attributes to
- use, like `dataclasses`. If False, it will only look for explicit
- :func:`field` class attributes, like classic *attrs*.
-
- If left None, it will guess:
-
- 1. If any attributes are annotated and no unannotated
- `attrs.field`\ s are found, it assumes *auto_attribs=True*.
- 2. Otherwise it assumes *auto_attribs=False* and tries to collect
- `attrs.field`\ s.
-
- If *attrs* decides to look at type annotations, **all** fields
- **must** be annotated. If *attrs* encounters a field that is set to
- a :func:`field` / `attr.ib` but lacks a type annotation, an
- `attrs.exceptions.UnannotatedAttributeError` is raised. Use
- ``field_name: typing.Any = field(...)`` if you don't want to set a
- type.
-
- .. warning::
-
- For features that use the attribute name to create decorators
- (for example, :ref:`validators `), you still *must*
- assign :func:`field` / `attr.ib` to them. Otherwise Python will
- either not find the name or try to use the default value to
- call, for example, ``validator`` on it.
-
- Attributes annotated as `typing.ClassVar`, and attributes that are
- neither annotated nor set to an `field()` are **ignored**.
-
- these (dict[str, object]):
- A dictionary of name to the (private) return value of `field()`
- mappings. This is useful to avoid the definition of your attributes
- within the class body because you can't (for example, 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*.
-
- Arguably, this is a rather obscure feature.
-
- .. 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).
- .. versionchanged:: 24.1.0
- Instances are not compared as tuples of attributes anymore, but using a
- big ``and`` condition. This is faster and has more correct behavior for
- uncomparable values like `math.nan`.
- .. versionadded:: 24.1.0
- If a class has an *inherited* classmethod called
- ``__attrs_init_subclass__``, it is executed after the class is created.
- .. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*.
- .. versionadded:: 24.3.0
- Unless already present, a ``__replace__`` method is automatically
- created for `copy.replace` (Python 3.13+ only).
-
- .. note::
-
- The main differences to the classic `attr.s` are:
-
- - 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*
-
- 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.
-
- """
-
- 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 = _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,
-):
- """
- Create a new :term:`field` / :term:`attribute` on a class.
-
- .. warning::
-
- Does **nothing** unless the class is also decorated with
- `attrs.define` (or similar)!
-
- Args:
- 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.
-
- .. seealso:: `defaults`
-
- factory (~typing.Callable):
- Syntactic sugar for ``default=attr.Factory(factory)``.
-
- validator (~typing.Callable | list[~typing.Callable]):
- 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`
-
- repr (bool | ~typing.Callable):
- 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,
- which means it will be used directly *instead* of calling
- ``repr()`` (the default).
-
- eq (bool | ~typing.Callable):
- 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`
-
- order (bool | ~typing.Callable):
- 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`
-
- hash (bool | None):
- 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`
-
- init (bool):
- 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`
-
- converter (typing.Callable | Converter):
- A callable that is called by *attrs*-generated ``__init__`` methods
- to convert attribute's value to the desired format.
-
- If a vanilla callable is passed, it is given the passed-in value as
- the only positional argument. It is possible to receive additional
- arguments by wrapping the callable in a `Converter`.
-
- Either way, 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`
-
- metadata (dict | None):
- An arbitrary mapping, to be used by third-party code.
-
- .. seealso:: `extending-metadata`.
-
- type (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 backwards-compatibility and for usage
- with `make_class`. 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 `.
-
- kw_only (bool):
- Make this attribute keyword-only in the generated ``__init__`` (if
- ``init`` is False, this parameter is ignored).
-
- on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]):
- 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 `define()`.
-
- alias (str | None):
- 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:: 20.1.0
- .. versionchanged:: 21.1.0
- *eq*, *order*, and *cmp* also accept a custom callable
- .. versionadded:: 22.2.0 *alias*
- .. versionadded:: 23.1.0
- The *type* parameter has been re-added; mostly for `attrs.make_class`.
- Please note that type checkers ignore this metadata.
-
- .. seealso::
-
- `attr.ib`
- """
- 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/myenv/Lib/site-packages/attr/_typing_compat.pyi b/myenv/Lib/site-packages/attr/_typing_compat.pyi
deleted file mode 100644
index ca7b71e..0000000
--- a/myenv/Lib/site-packages/attr/_typing_compat.pyi
+++ /dev/null
@@ -1,15 +0,0 @@
-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/myenv/Lib/site-packages/attr/_version_info.py b/myenv/Lib/site-packages/attr/_version_info.py
deleted file mode 100644
index 51a1312..0000000
--- a/myenv/Lib/site-packages/attr/_version_info.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# 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/myenv/Lib/site-packages/attr/_version_info.pyi b/myenv/Lib/site-packages/attr/_version_info.pyi
deleted file mode 100644
index 45ced08..0000000
--- a/myenv/Lib/site-packages/attr/_version_info.pyi
+++ /dev/null
@@ -1,9 +0,0 @@
-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/myenv/Lib/site-packages/attr/converters.py b/myenv/Lib/site-packages/attr/converters.py
deleted file mode 100644
index 0a79dee..0000000
--- a/myenv/Lib/site-packages/attr/converters.py
+++ /dev/null
@@ -1,162 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-"""
-Commonly useful converters.
-"""
-
-import typing
-
-from ._compat import _AnnotationExtractor
-from ._make import NOTHING, Converter, 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.
-
- Args:
- converter (typing.Callable):
- the converter that is used for non-`None` values.
-
- .. versionadded:: 17.1.0
- """
-
- if isinstance(converter, Converter):
-
- def optional_converter(val, inst, field):
- if val is None:
- return None
- return converter(val, inst, field)
-
- else:
-
- 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]
-
- if isinstance(converter, Converter):
- return Converter(optional_converter, takes_self=True, takes_field=True)
-
- 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*.
-
- Args:
- default:
- Value to be used if `None` is passed. Passing an instance of
- `attrs.Factory` is supported, however the ``takes_self`` option is
- *not*.
-
- factory (typing.Callable):
- A callable that takes no parameters whose result is used if `None`
- is passed.
-
- Raises:
- TypeError: If **neither** *default* or *factory* is passed.
-
- TypeError: If **both** *default* and *factory* are passed.
-
- 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 (for example, from environment variables) to real
- booleans.
-
- Values mapping to `True`:
-
- - ``True``
- - ``"true"`` / ``"t"``
- - ``"yes"`` / ``"y"``
- - ``"on"``
- - ``"1"``
- - ``1``
-
- Values mapping to `False`:
-
- - ``False``
- - ``"false"`` / ``"f"``
- - ``"no"`` / ``"n"``
- - ``"off"``
- - ``"0"``
- - ``0``
-
- Raises:
- ValueError: For any other value.
-
- .. versionadded:: 21.3.0
- """
- if isinstance(val, str):
- val = val.lower()
-
- if val in (True, "true", "t", "yes", "y", "on", "1", 1):
- return True
- if val in (False, "false", "f", "no", "n", "off", "0", 0):
- return False
-
- msg = f"Cannot convert value to bool: {val!r}"
- raise ValueError(msg)
diff --git a/myenv/Lib/site-packages/attr/converters.pyi b/myenv/Lib/site-packages/attr/converters.pyi
deleted file mode 100644
index 12bd0c4..0000000
--- a/myenv/Lib/site-packages/attr/converters.pyi
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Callable, Any, overload
-
-from attrs import _ConverterType, _CallableConverterType
-
-@overload
-def pipe(*validators: _CallableConverterType) -> _CallableConverterType: ...
-@overload
-def pipe(*validators: _ConverterType) -> _ConverterType: ...
-@overload
-def optional(converter: _CallableConverterType) -> _CallableConverterType: ...
-@overload
-def optional(converter: _ConverterType) -> _ConverterType: ...
-@overload
-def default_if_none(default: Any) -> _CallableConverterType: ...
-@overload
-def default_if_none(
- *, factory: Callable[[], Any]
-) -> _CallableConverterType: ...
-def to_bool(val: str | int | bool) -> bool: ...
diff --git a/myenv/Lib/site-packages/attr/exceptions.py b/myenv/Lib/site-packages/attr/exceptions.py
deleted file mode 100644
index 3b7abb8..0000000
--- a/myenv/Lib/site-packages/attr/exceptions.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# 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/myenv/Lib/site-packages/attr/exceptions.pyi b/myenv/Lib/site-packages/attr/exceptions.pyi
deleted file mode 100644
index f268011..0000000
--- a/myenv/Lib/site-packages/attr/exceptions.pyi
+++ /dev/null
@@ -1,17 +0,0 @@
-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/myenv/Lib/site-packages/attr/filters.py b/myenv/Lib/site-packages/attr/filters.py
deleted file mode 100644
index 689b170..0000000
--- a/myenv/Lib/site-packages/attr/filters.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-"""
-Commonly useful filters for `attrs.asdict` and `attrs.astuple`.
-"""
-
-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):
- """
- Create a filter that only allows *what*.
-
- Args:
- what (list[type, str, attrs.Attribute]):
- What to include. Can be a type, a name, or an attribute.
-
- Returns:
- Callable:
- A callable that can be passed to `attrs.asdict`'s and
- `attrs.astuple`'s *filter* argument.
-
- .. 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):
- """
- Create a filter that does **not** allow *what*.
-
- Args:
- what (list[type, str, attrs.Attribute]):
- What to exclude. Can be a type, a name, or an attribute.
-
- Returns:
- Callable:
- A callable that can be passed to `attrs.asdict`'s and
- `attrs.astuple`'s *filter* argument.
-
- .. 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/myenv/Lib/site-packages/attr/filters.pyi b/myenv/Lib/site-packages/attr/filters.pyi
deleted file mode 100644
index 974abdc..0000000
--- a/myenv/Lib/site-packages/attr/filters.pyi
+++ /dev/null
@@ -1,6 +0,0 @@
-from typing import Any
-
-from . import Attribute, _FilterType
-
-def include(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ...
-def exclude(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ...
diff --git a/myenv/Lib/site-packages/attr/py.typed b/myenv/Lib/site-packages/attr/py.typed
deleted file mode 100644
index e69de29..0000000
diff --git a/myenv/Lib/site-packages/attr/setters.py b/myenv/Lib/site-packages/attr/setters.py
deleted file mode 100644
index 78b0839..0000000
--- a/myenv/Lib/site-packages/attr/setters.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# 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:
- # This can be removed once we drop 3.8 and use attrs.Converter instead.
- from ._make import Converter
-
- if not isinstance(c, Converter):
- return c(new_value)
-
- return c(new_value, instance, attrib)
-
- return new_value
-
-
-# Sentinel for disabling class-wide *on_setattr* hooks for certain attributes.
-# Sphinx's autodata stopped working, so the docstring is inlined in the API
-# docs.
-NO_OP = object()
diff --git a/myenv/Lib/site-packages/attr/setters.pyi b/myenv/Lib/site-packages/attr/setters.pyi
deleted file mode 100644
index 73abf36..0000000
--- a/myenv/Lib/site-packages/attr/setters.pyi
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any, NewType, NoReturn, TypeVar
-
-from . import Attribute
-from attrs import _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/myenv/Lib/site-packages/attr/validators.py b/myenv/Lib/site-packages/attr/validators.py
deleted file mode 100644
index e7b7552..0000000
--- a/myenv/Lib/site-packages/attr/validators.py
+++ /dev/null
@@ -1,710 +0,0 @@
-# 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",
- "or_",
- "set_disabled",
-]
-
-
-def set_disabled(disabled):
- """
- Globally disable or enable running validators.
-
- By default, they are run.
-
- Args:
- disabled (bool): If `True`, disable running all validators.
-
- .. 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.
-
- Returns:
- bool:`True` if validators are currently disabled.
-
- .. 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, unsafe_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 = f"'{attr.name}' must be {self.type!r} (got {value!r} that is a {value.__class__!r})."
- 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).
-
- Args:
- type (type | tuple[type]): The type to check for.
-
- 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 = f"'{attr.name}' must match regex {self.pattern.pattern!r} ({value!r} doesn't)"
- 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*.
-
- Args:
- regex (str, re.Pattern):
- A regex string or precompiled pattern to match against
-
- flags (int):
- Flags that will be passed to the underlying re function (default 0)
-
- func (typing.Callable):
- 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, unsafe_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.
-
- Args:
- validator
- (typing.Callable | tuple[typing.Callable] | list[typing.Callable]):
- 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, unsafe_hash=True)
-class _InValidator:
- options = attrib()
- _original_options = attrib(hash=False)
-
- 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._original_options!r} (got {value!r})"
- raise ValueError(
- msg,
- attr,
- self._original_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``, so *options* has to
- support that operation.
-
- To keep the validator hashable, dicts, lists, and sets are transparently
- transformed into a `tuple`.
-
- Args:
- options: Allowed options.
-
- 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.
- .. versionchanged:: 24.1.0
- *options* that are a list, dict, or a set are now transformed into a
- tuple to keep the validator hashable.
- """
- repr_options = options
- if isinstance(options, (list, dict, set)):
- options = tuple(options)
-
- return _InValidator(options, repr_options)
-
-
-@attrs(repr=False, slots=False, unsafe_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, unsafe_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.
-
- Args:
- member_validator: Validator to apply to iterable members.
-
- iterable_validator:
- Validator to apply to iterable itself (optional).
-
- Raises
- TypeError: if any sub-validators fail
-
- .. versionadded:: 19.1.0
- """
- if isinstance(member_validator, (list, tuple)):
- member_validator = and_(*member_validator)
- return _DeepIterable(member_validator, iterable_validator)
-
-
-@attrs(repr=False, slots=True, unsafe_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 f""
-
-
-def deep_mapping(key_validator, value_validator, mapping_validator=None):
- """
- A validator that performs deep validation of a dictionary.
-
- Args:
- key_validator: Validator to apply to dictionary keys.
-
- value_validator: Validator to apply to dictionary values.
-
- 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*.
-
- The validator uses `operator.lt` to compare the values.
-
- Args:
- 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*.
-
- The validator uses `operator.le` to compare the values.
-
- Args:
- 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*.
-
- The validator uses `operator.ge` to compare the values.
-
- Args:
- 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*.
-
- The validator uses `operator.ge` to compare the values.
-
- Args:
- 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*.
-
- Args:
- length (int): 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*.
-
- Args:
- length (int): Minimum length of the string or iterable
-
- .. versionadded:: 22.1.0
- """
- return _MinLengthValidator(length)
-
-
-@attrs(repr=False, slots=True, unsafe_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).
-
- Args:
- type (type | tuple[type, ...]): The type(s) to check for.
-
- 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, unsafe_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 f""
-
-
-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_(...))``.
-
- Args:
- validator: A validator to be logically inverted.
-
- msg (str):
- Message to raise if validator fails. Formatted with keys
- ``exc_types`` and ``validator``.
-
- exc_types (tuple[type, ...]):
- 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)
-
-
-@attrs(repr=False, slots=True, unsafe_hash=True)
-class _OrValidator:
- validators = attrib()
-
- def __call__(self, inst, attr, value):
- for v in self.validators:
- try:
- v(inst, attr, value)
- except Exception: # noqa: BLE001, PERF203, S112
- continue
- else:
- return
-
- msg = f"None of {self.validators!r} satisfied for value {value!r}"
- raise ValueError(msg)
-
- def __repr__(self):
- return f""
-
-
-def or_(*validators):
- """
- A validator that composes multiple validators into one.
-
- When called on a value, it runs all wrapped validators until one of them is
- satisfied.
-
- Args:
- validators (~collections.abc.Iterable[typing.Callable]):
- Arbitrary number of validators.
-
- Raises:
- ValueError:
- If no validator is satisfied. Raised with a human-readable error
- message listing all the wrapped validators and the value that
- failed all of them.
-
- .. versionadded:: 24.1.0
- """
- vals = []
- for v in validators:
- vals.extend(v.validators if isinstance(v, _OrValidator) else [v])
-
- return _OrValidator(tuple(vals))
diff --git a/myenv/Lib/site-packages/attr/validators.pyi b/myenv/Lib/site-packages/attr/validators.pyi
deleted file mode 100644
index a0fdda7..0000000
--- a/myenv/Lib/site-packages/attr/validators.pyi
+++ /dev/null
@@ -1,86 +0,0 @@
-from types import UnionType
-from typing import (
- Any,
- AnyStr,
- Callable,
- Container,
- ContextManager,
- Iterable,
- Mapping,
- Match,
- Pattern,
- TypeVar,
- overload,
-)
-
-from attrs import _ValidatorType
-from attrs 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[_T1 | _T2]: ...
-@overload
-def instance_of(
- type: tuple[type[_T1], type[_T2], type[_T3]],
-) -> _ValidatorType[_T1 | _T2 | _T3]: ...
-@overload
-def instance_of(type: tuple[type, ...]) -> _ValidatorType[Any]: ...
-@overload
-def instance_of(type: UnionType) -> _ValidatorType[Any]: ...
-def optional(
- validator: (
- _ValidatorType[_T]
- | list[_ValidatorType[_T]]
- | tuple[_ValidatorType[_T]]
- ),
-) -> _ValidatorType[_T | None]: ...
-def in_(options: Container[_T]) -> _ValidatorType[_T]: ...
-def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...
-def matches_re(
- regex: Pattern[AnyStr] | AnyStr,
- flags: int = ...,
- func: Callable[[AnyStr, AnyStr, int], Match[AnyStr] | None] | None = ...,
-) -> _ValidatorType[AnyStr]: ...
-def deep_iterable(
- member_validator: _ValidatorArgType[_T],
- iterable_validator: _ValidatorType[_I] | None = ...,
-) -> _ValidatorType[_I]: ...
-def deep_mapping(
- key_validator: _ValidatorType[_K],
- value_validator: _ValidatorType[_V],
- mapping_validator: _ValidatorType[_M] | None = ...,
-) -> _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: str | None = None,
- exc_types: type[Exception] | Iterable[type[Exception]] = ...,
-) -> _ValidatorType[_T]: ...
-def or_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...
diff --git a/myenv/Lib/site-packages/attrs-25.3.0.dist-info/INSTALLER b/myenv/Lib/site-packages/attrs-25.3.0.dist-info/INSTALLER
deleted file mode 100644
index a1b589e..0000000
--- a/myenv/Lib/site-packages/attrs-25.3.0.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/myenv/Lib/site-packages/attrs-25.3.0.dist-info/METADATA b/myenv/Lib/site-packages/attrs-25.3.0.dist-info/METADATA
deleted file mode 100644
index 029afee..0000000
--- a/myenv/Lib/site-packages/attrs-25.3.0.dist-info/METADATA
+++ /dev/null
@@ -1,232 +0,0 @@
-Metadata-Version: 2.4
-Name: attrs
-Version: 25.3.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: 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 :: 3.13
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Typing :: Typed
-Requires-Python: >=3.8
-Provides-Extra: benchmark
-Requires-Dist: cloudpickle; (platform_python_implementation == 'CPython') and extra == 'benchmark'
-Requires-Dist: hypothesis; extra == 'benchmark'
-Requires-Dist: mypy>=1.11.1; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'benchmark'
-Requires-Dist: pympler; extra == 'benchmark'
-Requires-Dist: pytest-codspeed; extra == 'benchmark'
-Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'benchmark'
-Requires-Dist: pytest-xdist[psutil]; extra == 'benchmark'
-Requires-Dist: pytest>=4.3.0; extra == 'benchmark'
-Provides-Extra: cov
-Requires-Dist: cloudpickle; (platform_python_implementation == 'CPython') and extra == 'cov'
-Requires-Dist: coverage[toml]>=5.3; extra == 'cov'
-Requires-Dist: hypothesis; extra == 'cov'
-Requires-Dist: mypy>=1.11.1; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'cov'
-Requires-Dist: pympler; extra == 'cov'
-Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'cov'
-Requires-Dist: pytest-xdist[psutil]; extra == 'cov'
-Requires-Dist: pytest>=4.3.0; extra == 'cov'
-Provides-Extra: dev
-Requires-Dist: cloudpickle; (platform_python_implementation == 'CPython') and extra == 'dev'
-Requires-Dist: hypothesis; extra == 'dev'
-Requires-Dist: mypy>=1.11.1; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'dev'
-Requires-Dist: pre-commit-uv; extra == 'dev'
-Requires-Dist: pympler; extra == 'dev'
-Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'dev'
-Requires-Dist: pytest-xdist[psutil]; extra == 'dev'
-Requires-Dist: pytest>=4.3.0; extra == 'dev'
-Provides-Extra: docs
-Requires-Dist: cogapp; 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'
-Provides-Extra: tests
-Requires-Dist: cloudpickle; (platform_python_implementation == 'CPython') and extra == 'tests'
-Requires-Dist: hypothesis; extra == 'tests'
-Requires-Dist: mypy>=1.11.1; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'tests'
-Requires-Dist: pympler; extra == 'tests'
-Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'tests'
-Requires-Dist: pytest-xdist[psutil]; extra == 'tests'
-Requires-Dist: pytest>=4.3.0; extra == 'tests'
-Provides-Extra: tests-mypy
-Requires-Dist: mypy>=1.11.1; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'tests-mypy'
-Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.10') and extra == 'tests-mypy'
-Description-Content-Type: text/markdown
-
-
-
-
-*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.
-
----
-
-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**.
-
-Check out [*On The Core API Names*](https://www.attrs.org/en/latest/names.html) for an in-depth explanation!
-
-
-### Hate Type Annotations!?
-
-No problem!
-Types are entirely **optional** with *attrs*.
-Simply assign `attrs.field()` to the attributes instead of annotating them with types:
-
-```python
-from attrs import define, field
-
-@define
-class SomeClass:
- a_number = field(default=42)
- list_of_numbers = field(factory=list)
-```
-
-
-## 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), has a replacement for `__init_subclass__`, 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), but generally speaking, we are more likely to commit crimes against nature to make things work that one would expect to work, but that are quite complicated in practice.
-
-
-## 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**: use the `python-attrs` tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/python-attrs)
-
-
-### *attrs* for Enterprise
-
-Available as part of the [Tidelift Subscription](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek).
-
-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.
-
-## Release Information
-
-### Changes
-
-- Restore support for generator-based `field_transformer`s.
- [#1417](https://github.com/python-attrs/attrs/issues/1417)
-
-
-
----
-
-[Full changelog →](https://www.attrs.org/en/stable/changelog.html)
diff --git a/myenv/Lib/site-packages/attrs-25.3.0.dist-info/RECORD b/myenv/Lib/site-packages/attrs-25.3.0.dist-info/RECORD
deleted file mode 100644
index 9cc9074..0000000
--- a/myenv/Lib/site-packages/attrs-25.3.0.dist-info/RECORD
+++ /dev/null
@@ -1,55 +0,0 @@
-attr/__init__.py,sha256=fOYIvt1eGSqQre4uCS3sJWKZ0mwAuC8UD6qba5OS9_U,2057
-attr/__init__.pyi,sha256=QIXnnHPoucmDWkbpNsWTP-cgJ1bn8le7DjyRa_wYdew,11281
-attr/__pycache__/__init__.cpython-38.pyc,,
-attr/__pycache__/_cmp.cpython-38.pyc,,
-attr/__pycache__/_compat.cpython-38.pyc,,
-attr/__pycache__/_config.cpython-38.pyc,,
-attr/__pycache__/_funcs.cpython-38.pyc,,
-attr/__pycache__/_make.cpython-38.pyc,,
-attr/__pycache__/_next_gen.cpython-38.pyc,,
-attr/__pycache__/_version_info.cpython-38.pyc,,
-attr/__pycache__/converters.cpython-38.pyc,,
-attr/__pycache__/exceptions.cpython-38.pyc,,
-attr/__pycache__/filters.cpython-38.pyc,,
-attr/__pycache__/setters.cpython-38.pyc,,
-attr/__pycache__/validators.cpython-38.pyc,,
-attr/_cmp.py,sha256=3Nn1TjxllUYiX_nJoVnEkXoDk0hM1DYKj5DE7GZe4i0,4117
-attr/_cmp.pyi,sha256=U-_RU_UZOyPUEQzXE6RMYQQcjkZRY25wTH99sN0s7MM,368
-attr/_compat.py,sha256=4hlXbWhdDjQCDK6FKF1EgnZ3POiHgtpp54qE0nxaGHg,2704
-attr/_config.py,sha256=dGq3xR6fgZEF6UBt_L0T-eUHIB4i43kRmH0P28sJVw8,843
-attr/_funcs.py,sha256=5-tUKJtp3h5El55EcDl6GWXFp68fT8D8U7uCRN6497I,15854
-attr/_make.py,sha256=lBUPPmxiA1BeHzB6OlHoCEh--tVvM1ozXO8eXOa6g4c,96664
-attr/_next_gen.py,sha256=7FRkbtl_N017SuBhf_Vw3mw2c2pGZhtCGOzadgz7tp4,24395
-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=GlDeOzPeTFgeBBLbj9G57Ez5lAk68uhSALRYJ_exe84,3861
-attr/converters.pyi,sha256=orU2bff-VjQa2kMDyvnMQV73oJT2WRyQuw4ZR1ym1bE,643
-attr/exceptions.py,sha256=HRFq4iybmv7-DcZwyjl6M1euM2YeJVK_hFxuaBGAngI,1977
-attr/exceptions.pyi,sha256=zZq8bCUnKAy9mDtBEw42ZhPhAUIHoTKedDQInJD883M,539
-attr/filters.py,sha256=ZBiKWLp3R0LfCZsq7X11pn9WX8NslS2wXM4jsnLOGc8,1795
-attr/filters.pyi,sha256=3J5BG-dTxltBk1_-RuNRUHrv2qu1v8v4aDNAQ7_mifA,208
-attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-attr/setters.py,sha256=5-dcT63GQK35ONEzSgfXCkbB7pPkaR-qv15mm4PVSzQ,1617
-attr/setters.pyi,sha256=NnVkaFU1BB4JB8E4JuXyrzTUgvtMpj8p3wBdJY7uix4,584
-attr/validators.py,sha256=WaB1HLAHHqRHWsrv_K9H-sJ7ESil3H3Cmv2d8TtVZx4,20046
-attr/validators.pyi,sha256=s2WhKPqskxbsckJfKk8zOuuB088GfgpyxcCYSNFLqNU,2603
-attrs-25.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-attrs-25.3.0.dist-info/METADATA,sha256=W38cREj7s1wqNf1fg4hVwZmL1xh0AdSp4IhtTMROinw,10993
-attrs-25.3.0.dist-info/RECORD,,
-attrs-25.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
-attrs-25.3.0.dist-info/licenses/LICENSE,sha256=iCEVyV38KvHutnFPjsbVy8q_Znyv-HKfQkINpj9xTp8,1109
-attrs/__init__.py,sha256=qeQJZ4O08yczSn840v9bYOaZyRE81WsVi-QCrY3krCU,1107
-attrs/__init__.pyi,sha256=nZmInocjM7tHV4AQw0vxO_fo6oJjL_PonlV9zKKW8DY,7931
-attrs/__pycache__/__init__.cpython-38.pyc,,
-attrs/__pycache__/converters.cpython-38.pyc,,
-attrs/__pycache__/exceptions.cpython-38.pyc,,
-attrs/__pycache__/filters.cpython-38.pyc,,
-attrs/__pycache__/setters.cpython-38.pyc,,
-attrs/__pycache__/validators.cpython-38.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/myenv/Lib/site-packages/attrs-25.3.0.dist-info/WHEEL b/myenv/Lib/site-packages/attrs-25.3.0.dist-info/WHEEL
deleted file mode 100644
index 12228d4..0000000
--- a/myenv/Lib/site-packages/attrs-25.3.0.dist-info/WHEEL
+++ /dev/null
@@ -1,4 +0,0 @@
-Wheel-Version: 1.0
-Generator: hatchling 1.27.0
-Root-Is-Purelib: true
-Tag: py3-none-any
diff --git a/myenv/Lib/site-packages/attrs-25.3.0.dist-info/licenses/LICENSE b/myenv/Lib/site-packages/attrs-25.3.0.dist-info/licenses/LICENSE
deleted file mode 100644
index 2bd6453..0000000
--- a/myenv/Lib/site-packages/attrs-25.3.0.dist-info/licenses/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-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/myenv/Lib/site-packages/attrs/__init__.py b/myenv/Lib/site-packages/attrs/__init__.py
deleted file mode 100644
index e8023ff..0000000
--- a/myenv/Lib/site-packages/attrs/__init__.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-from attr import (
- NOTHING,
- Attribute,
- AttrsInstance,
- Converter,
- Factory,
- NothingType,
- _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__ = [
- "NOTHING",
- "Attribute",
- "AttrsInstance",
- "Converter",
- "Factory",
- "NothingType",
- "__author__",
- "__copyright__",
- "__description__",
- "__doc__",
- "__email__",
- "__license__",
- "__title__",
- "__url__",
- "__version__",
- "__version_info__",
- "asdict",
- "assoc",
- "astuple",
- "cmp_using",
- "converters",
- "define",
- "evolve",
- "exceptions",
- "field",
- "fields",
- "fields_dict",
- "filters",
- "frozen",
- "has",
- "make_class",
- "mutable",
- "resolve_types",
- "setters",
- "validate",
- "validators",
-]
-
-__getattr__ = _make_getattr(__name__)
diff --git a/myenv/Lib/site-packages/attrs/__init__.pyi b/myenv/Lib/site-packages/attrs/__init__.pyi
deleted file mode 100644
index 648fa7a..0000000
--- a/myenv/Lib/site-packages/attrs/__init__.pyi
+++ /dev/null
@@ -1,263 +0,0 @@
-import sys
-
-from typing import (
- Any,
- Callable,
- Mapping,
- Sequence,
- overload,
- TypeVar,
-)
-
-# 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 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 Converter as Converter
-from attr import evolve as evolve
-from attr import exceptions as exceptions
-from attr import Factory as Factory
-from attr import fields as fields
-from attr import fields_dict as fields_dict
-from attr import filters as filters
-from attr import has as has
-from attr import make_class as make_class
-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
-from attr import attrib, asdict as asdict, astuple as astuple
-from attr import NothingType as NothingType
-
-if sys.version_info >= (3, 11):
- from typing import dataclass_transform
-else:
- from typing_extensions import dataclass_transform
-
-_T = TypeVar("_T")
-_C = TypeVar("_C", bound=type)
-
-_EqOrderType = bool | Callable[[Any], Any]
-_ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any]
-_CallableConverterType = Callable[[Any], Any]
-_ConverterType = _CallableConverterType | Converter[Any, Any]
-_ReprType = Callable[[Any], str]
-_ReprArgType = bool | _ReprType
-_OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any]
-_OnSetAttrArgType = _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 = _ValidatorType[_T] | Sequence[_ValidatorType[_T]]
-
-@overload
-def field(
- *,
- default: None = ...,
- validator: None = ...,
- repr: _ReprArgType = ...,
- hash: bool | None = ...,
- init: bool = ...,
- metadata: Mapping[Any, Any] | None = ...,
- converter: None = ...,
- factory: None = ...,
- kw_only: bool = ...,
- eq: bool | None = ...,
- order: bool | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- alias: str | None = ...,
- type: type | None = ...,
-) -> Any: ...
-
-# This form catches an explicit None or no default and infers the type from the
-# other arguments.
-@overload
-def field(
- *,
- default: None = ...,
- validator: _ValidatorArgType[_T] | None = ...,
- repr: _ReprArgType = ...,
- hash: bool | None = ...,
- init: bool = ...,
- metadata: Mapping[Any, Any] | None = ...,
- converter: _ConverterType
- | list[_ConverterType]
- | tuple[_ConverterType]
- | None = ...,
- factory: Callable[[], _T] | None = ...,
- kw_only: bool = ...,
- eq: _EqOrderType | None = ...,
- order: _EqOrderType | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- alias: str | None = ...,
- type: type | None = ...,
-) -> _T: ...
-
-# This form catches an explicit default argument.
-@overload
-def field(
- *,
- default: _T,
- validator: _ValidatorArgType[_T] | None = ...,
- repr: _ReprArgType = ...,
- hash: bool | None = ...,
- init: bool = ...,
- metadata: Mapping[Any, Any] | None = ...,
- converter: _ConverterType
- | list[_ConverterType]
- | tuple[_ConverterType]
- | None = ...,
- factory: Callable[[], _T] | None = ...,
- kw_only: bool = ...,
- eq: _EqOrderType | None = ...,
- order: _EqOrderType | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- alias: str | None = ...,
- type: type | None = ...,
-) -> _T: ...
-
-# This form covers type=non-Type: e.g. forward references (str), Any
-@overload
-def field(
- *,
- default: _T | None = ...,
- validator: _ValidatorArgType[_T] | None = ...,
- repr: _ReprArgType = ...,
- hash: bool | None = ...,
- init: bool = ...,
- metadata: Mapping[Any, Any] | None = ...,
- converter: _ConverterType
- | list[_ConverterType]
- | tuple[_ConverterType]
- | None = ...,
- factory: Callable[[], _T] | None = ...,
- kw_only: bool = ...,
- eq: _EqOrderType | None = ...,
- order: _EqOrderType | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- alias: str | None = ...,
- type: type | None = ...,
-) -> Any: ...
-@overload
-@dataclass_transform(field_specifiers=(attrib, field))
-def define(
- maybe_cls: _C,
- *,
- these: dict[str, Any] | None = ...,
- repr: bool = ...,
- unsafe_hash: bool | None = ...,
- hash: bool | None = ...,
- init: bool = ...,
- slots: bool = ...,
- frozen: bool = ...,
- weakref_slot: bool = ...,
- str: bool = ...,
- auto_attribs: bool = ...,
- kw_only: bool = ...,
- cache_hash: bool = ...,
- auto_exc: bool = ...,
- eq: bool | None = ...,
- order: bool | None = ...,
- auto_detect: bool = ...,
- getstate_setstate: bool | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- field_transformer: _FieldTransformer | None = ...,
- match_args: bool = ...,
-) -> _C: ...
-@overload
-@dataclass_transform(field_specifiers=(attrib, field))
-def define(
- maybe_cls: None = ...,
- *,
- these: dict[str, Any] | None = ...,
- repr: bool = ...,
- unsafe_hash: bool | None = ...,
- hash: bool | None = ...,
- init: bool = ...,
- slots: bool = ...,
- frozen: bool = ...,
- weakref_slot: bool = ...,
- str: bool = ...,
- auto_attribs: bool = ...,
- kw_only: bool = ...,
- cache_hash: bool = ...,
- auto_exc: bool = ...,
- eq: bool | None = ...,
- order: bool | None = ...,
- auto_detect: bool = ...,
- getstate_setstate: bool | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- field_transformer: _FieldTransformer | None = ...,
- match_args: bool = ...,
-) -> Callable[[_C], _C]: ...
-
-mutable = define
-
-@overload
-@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field))
-def frozen(
- maybe_cls: _C,
- *,
- these: dict[str, Any] | None = ...,
- repr: bool = ...,
- unsafe_hash: bool | None = ...,
- hash: bool | None = ...,
- init: bool = ...,
- slots: bool = ...,
- frozen: bool = ...,
- weakref_slot: bool = ...,
- str: bool = ...,
- auto_attribs: bool = ...,
- kw_only: bool = ...,
- cache_hash: bool = ...,
- auto_exc: bool = ...,
- eq: bool | None = ...,
- order: bool | None = ...,
- auto_detect: bool = ...,
- getstate_setstate: bool | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- field_transformer: _FieldTransformer | None = ...,
- match_args: bool = ...,
-) -> _C: ...
-@overload
-@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field))
-def frozen(
- maybe_cls: None = ...,
- *,
- these: dict[str, Any] | None = ...,
- repr: bool = ...,
- unsafe_hash: bool | None = ...,
- hash: bool | None = ...,
- init: bool = ...,
- slots: bool = ...,
- frozen: bool = ...,
- weakref_slot: bool = ...,
- str: bool = ...,
- auto_attribs: bool = ...,
- kw_only: bool = ...,
- cache_hash: bool = ...,
- auto_exc: bool = ...,
- eq: bool | None = ...,
- order: bool | None = ...,
- auto_detect: bool = ...,
- getstate_setstate: bool | None = ...,
- on_setattr: _OnSetAttrArgType | None = ...,
- field_transformer: _FieldTransformer | None = ...,
- match_args: bool = ...,
-) -> Callable[[_C], _C]: ...
diff --git a/myenv/Lib/site-packages/attrs/__pycache__/__init__.cpython-38.pyc b/myenv/Lib/site-packages/attrs/__pycache__/__init__.cpython-38.pyc
deleted file mode 100644
index 48acccf..0000000
Binary files a/myenv/Lib/site-packages/attrs/__pycache__/__init__.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attrs/__pycache__/converters.cpython-38.pyc b/myenv/Lib/site-packages/attrs/__pycache__/converters.cpython-38.pyc
deleted file mode 100644
index 25810c3..0000000
Binary files a/myenv/Lib/site-packages/attrs/__pycache__/converters.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attrs/__pycache__/exceptions.cpython-38.pyc b/myenv/Lib/site-packages/attrs/__pycache__/exceptions.cpython-38.pyc
deleted file mode 100644
index 35f6b58..0000000
Binary files a/myenv/Lib/site-packages/attrs/__pycache__/exceptions.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attrs/__pycache__/filters.cpython-38.pyc b/myenv/Lib/site-packages/attrs/__pycache__/filters.cpython-38.pyc
deleted file mode 100644
index 5fda9ff..0000000
Binary files a/myenv/Lib/site-packages/attrs/__pycache__/filters.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attrs/__pycache__/setters.cpython-38.pyc b/myenv/Lib/site-packages/attrs/__pycache__/setters.cpython-38.pyc
deleted file mode 100644
index b76ed8b..0000000
Binary files a/myenv/Lib/site-packages/attrs/__pycache__/setters.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attrs/__pycache__/validators.cpython-38.pyc b/myenv/Lib/site-packages/attrs/__pycache__/validators.cpython-38.pyc
deleted file mode 100644
index 6926b6a..0000000
Binary files a/myenv/Lib/site-packages/attrs/__pycache__/validators.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/attrs/converters.py b/myenv/Lib/site-packages/attrs/converters.py
deleted file mode 100644
index 7821f6c..0000000
--- a/myenv/Lib/site-packages/attrs/converters.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-from attr.converters import * # noqa: F403
diff --git a/myenv/Lib/site-packages/attrs/exceptions.py b/myenv/Lib/site-packages/attrs/exceptions.py
deleted file mode 100644
index 3323f9d..0000000
--- a/myenv/Lib/site-packages/attrs/exceptions.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-from attr.exceptions import * # noqa: F403
diff --git a/myenv/Lib/site-packages/attrs/filters.py b/myenv/Lib/site-packages/attrs/filters.py
deleted file mode 100644
index 3080f48..0000000
--- a/myenv/Lib/site-packages/attrs/filters.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-from attr.filters import * # noqa: F403
diff --git a/myenv/Lib/site-packages/attrs/py.typed b/myenv/Lib/site-packages/attrs/py.typed
deleted file mode 100644
index e69de29..0000000
diff --git a/myenv/Lib/site-packages/attrs/setters.py b/myenv/Lib/site-packages/attrs/setters.py
deleted file mode 100644
index f3d73bb..0000000
--- a/myenv/Lib/site-packages/attrs/setters.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-from attr.setters import * # noqa: F403
diff --git a/myenv/Lib/site-packages/attrs/validators.py b/myenv/Lib/site-packages/attrs/validators.py
deleted file mode 100644
index 037e124..0000000
--- a/myenv/Lib/site-packages/attrs/validators.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-from attr.validators import * # noqa: F403
diff --git a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/INSTALLER b/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/INSTALLER
deleted file mode 100644
index a1b589e..0000000
--- a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA b/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA
deleted file mode 100644
index 7fd97b7..0000000
--- a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA
+++ /dev/null
@@ -1,123 +0,0 @@
-Metadata-Version: 2.4
-Name: beautifulsoup4
-Version: 4.14.3
-Summary: Screen-scraping library
-Project-URL: Download, https://www.crummy.com/software/BeautifulSoup/bs4/download/
-Project-URL: Homepage, https://www.crummy.com/software/BeautifulSoup/bs4/
-Author-email: Leonard Richardson
-License: MIT License
-License-File: AUTHORS
-License-File: LICENSE
-Keywords: HTML,XML,parse,soup
-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
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Classifier: Topic :: Text Processing :: Markup :: HTML
-Classifier: Topic :: Text Processing :: Markup :: SGML
-Classifier: Topic :: Text Processing :: Markup :: XML
-Requires-Python: >=3.7.0
-Requires-Dist: soupsieve>=1.6.1
-Requires-Dist: typing-extensions>=4.0.0
-Provides-Extra: cchardet
-Requires-Dist: cchardet; extra == 'cchardet'
-Provides-Extra: chardet
-Requires-Dist: chardet; extra == 'chardet'
-Provides-Extra: charset-normalizer
-Requires-Dist: charset-normalizer; extra == 'charset-normalizer'
-Provides-Extra: html5lib
-Requires-Dist: html5lib; extra == 'html5lib'
-Provides-Extra: lxml
-Requires-Dist: lxml; extra == 'lxml'
-Description-Content-Type: text/markdown
-
-Beautiful Soup is a library that makes it easy to scrape information
-from web pages. It sits atop an HTML or XML parser, providing Pythonic
-idioms for iterating, searching, and modifying the parse tree.
-
-# Quick start
-
-```
->>> from bs4 import BeautifulSoup
->>> soup = BeautifulSoup("
SomebadHTML")
->>> print(soup.prettify())
-
-
-
- Some
-
- bad
-
- HTML
-
-
-
-
-
->>> soup.find(string="bad")
-'bad'
->>> soup.i
-HTML
-#
->>> soup = BeautifulSoup("SomebadXML", "xml")
-#
->>> print(soup.prettify())
-
-
- Some
-
- bad
-
- XML
-
-
-```
-
-To go beyond the basics, [comprehensive documentation is available](https://www.crummy.com/software/BeautifulSoup/bs4/doc/).
-
-# Links
-
-* [Homepage](https://www.crummy.com/software/BeautifulSoup/bs4/)
-* [Documentation](https://www.crummy.com/software/BeautifulSoup/bs4/doc/)
-* [Discussion group](https://groups.google.com/group/beautifulsoup/)
-* [Development](https://code.launchpad.net/beautifulsoup/)
-* [Bug tracker](https://bugs.launchpad.net/beautifulsoup/)
-* [Complete changelog](https://git.launchpad.net/beautifulsoup/tree/CHANGELOG)
-
-# Note on Python 2 sunsetting
-
-Beautiful Soup's support for Python 2 was discontinued on December 31,
-2020: one year after the sunset date for Python 2 itself. From this
-point onward, new Beautiful Soup development will exclusively target
-Python 3. The final release of Beautiful Soup 4 to support Python 2
-was 4.9.3.
-
-# Supporting the project
-
-If you use Beautiful Soup as part of your professional work, please consider a
-[Tidelift subscription](https://tidelift.com/subscription/pkg/pypi-beautifulsoup4?utm_source=pypi-beautifulsoup4&utm_medium=referral&utm_campaign=readme).
-This will support many of the free software projects your organization
-depends on, not just Beautiful Soup.
-
-If you use Beautiful Soup for personal projects, the best way to say
-thank you is to read
-[Tool Safety](https://www.crummy.com/software/BeautifulSoup/zine/), a zine I
-wrote about what Beautiful Soup has taught me about software
-development.
-
-# Building the documentation
-
-The bs4/doc/ directory contains full documentation in Sphinx
-format. Run `make html` in that directory to create HTML
-documentation.
-
-# Running the unit tests
-
-Beautiful Soup supports unit test discovery using Pytest:
-
-```
-$ pytest
-```
-
diff --git a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD b/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD
deleted file mode 100644
index 037f885..0000000
--- a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD
+++ /dev/null
@@ -1,37 +0,0 @@
-beautifulsoup4-4.14.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-beautifulsoup4-4.14.3.dist-info/METADATA,sha256=Ac93vA8Xp9FtgOcKXFM8ESfVdztimUfJ3WUpVlhKtsY,3812
-beautifulsoup4-4.14.3.dist-info/RECORD,,
-beautifulsoup4-4.14.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
-beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS,sha256=uYkjiRjh_aweRnF8tAW2PpJJeickE68NmJwd9siry28,2201
-beautifulsoup4-4.14.3.dist-info/licenses/LICENSE,sha256=VbTY1LHlvIbRDvrJG3TIe8t3UmsPW57a-LnNKtxzl7I,1441
-bs4/__init__.py,sha256=E7wiVp7oQK0JhdAYxpehZa8drv3W_sJv5oeTFiBfR5o,44386
-bs4/__pycache__/__init__.cpython-38.pyc,,
-bs4/__pycache__/_deprecation.cpython-38.pyc,,
-bs4/__pycache__/_typing.cpython-38.pyc,,
-bs4/__pycache__/_warnings.cpython-38.pyc,,
-bs4/__pycache__/css.cpython-38.pyc,,
-bs4/__pycache__/dammit.cpython-38.pyc,,
-bs4/__pycache__/diagnose.cpython-38.pyc,,
-bs4/__pycache__/element.cpython-38.pyc,,
-bs4/__pycache__/exceptions.cpython-38.pyc,,
-bs4/__pycache__/filter.cpython-38.pyc,,
-bs4/__pycache__/formatter.cpython-38.pyc,,
-bs4/_deprecation.py,sha256=niHJCk37APg8KEuFOa57ZXaxLdBmc_2V6uuaJqu7r30,2408
-bs4/_typing.py,sha256=zNcx7R1yCTK8WwtumP28hc7CJ3pMyZXj_VAeYaNXMZA,7549
-bs4/_warnings.py,sha256=ZuOETgcnEbZgw2N0nnNXn6wvtrn2ut7AF0d98bvkMFc,4711
-bs4/builder/__init__.py,sha256=Rl4qjOXvdyyyjayOFqbkgoUoo81IgoyKD-RwWeVK59g,31194
-bs4/builder/__pycache__/__init__.cpython-38.pyc,,
-bs4/builder/__pycache__/_html5lib.cpython-38.pyc,,
-bs4/builder/__pycache__/_htmlparser.cpython-38.pyc,,
-bs4/builder/__pycache__/_lxml.cpython-38.pyc,,
-bs4/builder/_html5lib.py,sha256=hL6xUk4_I2i5CMguFoYFlrI26cY4Dut7fOEQrUctHIM,23607
-bs4/builder/_htmlparser.py,sha256=CnULPQV2rm4vLojJABpQ7Xm9diddnEZx2Wcz_VTC1Mg,17445
-bs4/builder/_lxml.py,sha256=ks1e8boA_nOA2oomAhxeudccR6ThbEE-EllFqHRoPLA,18969
-bs4/css.py,sha256=_m_l_4SGWHnY620VJ21j_qQH1RX3p91sYVemgKxaLsM,12713
-bs4/dammit.py,sha256=ZJWa9K32X6N2imFHleqUq0ekf592weU1lvULN_WYWYk,57024
-bs4/diagnose.py,sha256=at98iuxyOrqec4V8iwkTIbNUqBCsq9Lr3fDAQx2129Y,7846
-bs4/element.py,sha256=oXmj7LG_2NpsDK90mq73q0PMK0FjFBIGSeTTJLVwwTc,120237
-bs4/exceptions.py,sha256=Q9FOadNe8QRvzDMaKSXe2Wtl8JK_oAZW7mbFZBVP_GE,951
-bs4/filter.py,sha256=rw8ZNhTDLEJVCEiSifou5tZR_3zBLeuvAyouY82qU_E,29201
-bs4/formatter.py,sha256=uBT0k6W8O5kJ9PCuJYjra97yoUqC-dlM9D_v-oRM0r8,10478
-bs4/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
diff --git a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL b/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL
deleted file mode 100644
index 12228d4..0000000
--- a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL
+++ /dev/null
@@ -1,4 +0,0 @@
-Wheel-Version: 1.0
-Generator: hatchling 1.27.0
-Root-Is-Purelib: true
-Tag: py3-none-any
diff --git a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS b/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS
deleted file mode 100644
index 18926c2..0000000
--- a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS
+++ /dev/null
@@ -1,49 +0,0 @@
-Behold, mortal, the origins of Beautiful Soup...
-================================================
-
-Leonard Richardson is the primary maintainer.
-
-Aaron DeVore, Isaac Muse and Chris Papademetrious have made
-significant contributions to the code base.
-
-Mark Pilgrim provided the encoding detection code that forms the base
-of UnicodeDammit.
-
-Thomas Kluyver and Ezio Melotti finished the work of getting Beautiful
-Soup 4 working under Python 3.
-
-Simon Willison wrote soupselect, which was used to make Beautiful Soup
-support CSS selectors. Isaac Muse wrote SoupSieve, which made it
-possible to _remove_ the CSS selector code from Beautiful Soup.
-
-Sam Ruby helped with a lot of edge cases.
-
-Jonathan Ellis was awarded the prestigious Beau Potage D'Or for his
-work in solving the nestable tags conundrum.
-
-An incomplete list of people have contributed patches to Beautiful
-Soup:
-
- Istvan Albert, Andrew Lin, Anthony Baxter, Oliver Beattie, Andrew
-Boyko, Tony Chang, Francisco Canas, "Delong", Zephyr Fang, Fuzzy,
-Roman Gaufman, Yoni Gilad, Richie Hindle, Toshihiro Kamiya, Peteris
-Krumins, Kent Johnson, Marek Kapolka, Andreas Kostyrka, Roel Kramer,
-Ben Last, Robert Leftwich, Stefaan Lippens, "liquider", Staffan
-Malmgren, Ksenia Marasanova, JP Moins, Adam Monsen, John Nagle, "Jon",
-Ed Oskiewicz, Martijn Peters, Greg Phillips, Giles Radford, Stefano
-Revera, Arthur Rudolph, Marko Samastur, James Salter, Jouni Seppänen,
-Alexander Schmolck, Tim Shirley, Geoffrey Sneddon, Ville Skyttä,
-"Vikas", Jens Svalgaard, Andy Theyers, Eric Weiser, Glyn Webster, John
-Wiseman, Paul Wright, Danny Yoo
-
-An incomplete list of people who made suggestions or found bugs or
-found ways to break Beautiful Soup:
-
- Hanno Böck, Matteo Bertini, Chris Curvey, Simon Cusack, Bruce Eckel,
- Matt Ernst, Michael Foord, Tom Harris, Bill de hOra, Donald Howes,
- Matt Patterson, Scott Roberts, Steve Strassmann, Mike Williams,
- warchild at redho dot com, Sami Kuisma, Carlos Rocha, Bob Hutchison,
- Joren Mc, Michal Migurski, John Kleven, Tim Heaney, Tripp Lilley, Ed
- Summers, Dennis Sutch, Chris Smith, Aaron Swartz, Stuart
- Turner, Greg Edwards, Kevin J Kalupson, Nikos Kouremenos, Artur de
- Sousa Rocha, Yichun Wei, Per Vognsen
diff --git a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE b/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE
deleted file mode 100644
index 08e3a9c..0000000
--- a/myenv/Lib/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE
+++ /dev/null
@@ -1,31 +0,0 @@
-Beautiful Soup is made available under the MIT license:
-
- Copyright (c) Leonard Richardson
-
- 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.
-
-Beautiful Soup incorporates code from the html5lib library, which is
-also made available under the MIT license. Copyright (c) James Graham
-and other contributors
-
-Beautiful Soup has an optional dependency on the soupsieve library,
-which is also made available under the MIT license. Copyright (c)
-Isaac Muse
diff --git a/myenv/Lib/site-packages/blinker-1.8.2.dist-info/INSTALLER b/myenv/Lib/site-packages/blinker-1.8.2.dist-info/INSTALLER
deleted file mode 100644
index a1b589e..0000000
--- a/myenv/Lib/site-packages/blinker-1.8.2.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/myenv/Lib/site-packages/blinker-1.8.2.dist-info/LICENSE.txt b/myenv/Lib/site-packages/blinker-1.8.2.dist-info/LICENSE.txt
deleted file mode 100644
index 79c9825..0000000
--- a/myenv/Lib/site-packages/blinker-1.8.2.dist-info/LICENSE.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright 2010 Jason Kirtland
-
-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/myenv/Lib/site-packages/blinker-1.8.2.dist-info/METADATA b/myenv/Lib/site-packages/blinker-1.8.2.dist-info/METADATA
deleted file mode 100644
index efa45f5..0000000
--- a/myenv/Lib/site-packages/blinker-1.8.2.dist-info/METADATA
+++ /dev/null
@@ -1,60 +0,0 @@
-Metadata-Version: 2.1
-Name: blinker
-Version: 1.8.2
-Summary: Fast, simple object-to-object and broadcast signaling
-Author: Jason Kirtland
-Maintainer-email: Pallets Ecosystem
-Requires-Python: >=3.8
-Description-Content-Type: text/markdown
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python
-Classifier: Typing :: Typed
-Project-URL: Chat, https://discord.gg/pallets
-Project-URL: Documentation, https://blinker.readthedocs.io
-Project-URL: Source, https://github.com/pallets-eco/blinker/
-
-# Blinker
-
-Blinker provides a fast dispatching system that allows any number of
-interested parties to subscribe to events, or "signals".
-
-
-## Pallets Community Ecosystem
-
-> [!IMPORTANT]\
-> This project is part of the Pallets Community Ecosystem. Pallets is the open
-> source organization that maintains Flask; Pallets-Eco enables community
-> maintenance of related projects. If you are interested in helping maintain
-> this project, please reach out on [the Pallets Discord server][discord].
->
-> [discord]: https://discord.gg/pallets
-
-
-## Example
-
-Signal receivers can subscribe to specific senders or receive signals
-sent by any sender.
-
-```pycon
->>> from blinker import signal
->>> started = signal('round-started')
->>> def each(round):
-... print(f"Round {round}")
-...
->>> started.connect(each)
-
->>> def round_two(round):
-... print("This is round two.")
-...
->>> started.connect(round_two, sender=2)
-
->>> for round in range(1, 4):
-... started.send(round)
-...
-Round 1!
-Round 2!
-This is round two.
-Round 3!
-```
-
diff --git a/myenv/Lib/site-packages/blinker-1.8.2.dist-info/RECORD b/myenv/Lib/site-packages/blinker-1.8.2.dist-info/RECORD
deleted file mode 100644
index 487f9e3..0000000
--- a/myenv/Lib/site-packages/blinker-1.8.2.dist-info/RECORD
+++ /dev/null
@@ -1,12 +0,0 @@
-blinker-1.8.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-blinker-1.8.2.dist-info/LICENSE.txt,sha256=nrc6HzhZekqhcCXSrhvjg5Ykx5XphdTw6Xac4p-spGc,1054
-blinker-1.8.2.dist-info/METADATA,sha256=3tEx40hm9IEofyFqDPJsDPE9MAIEhtifapoSp7FqzuA,1633
-blinker-1.8.2.dist-info/RECORD,,
-blinker-1.8.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
-blinker/__init__.py,sha256=ymyJY_PoTgBzaPgdr4dq-RRsGh7D-sYQIGMNp8Rx4qc,1577
-blinker/__pycache__/__init__.cpython-38.pyc,,
-blinker/__pycache__/_utilities.cpython-38.pyc,,
-blinker/__pycache__/base.cpython-38.pyc,,
-blinker/_utilities.py,sha256=0J7eeXXTUx0Ivf8asfpx0ycVkp0Eqfqnj117x2mYX9E,1675
-blinker/base.py,sha256=nIZJEtXQ8LLZZJrwVp2wQcdfCzDixvAHR9VpSWiyVcQ,22574
-blinker/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
diff --git a/myenv/Lib/site-packages/blinker-1.8.2.dist-info/WHEEL b/myenv/Lib/site-packages/blinker-1.8.2.dist-info/WHEEL
deleted file mode 100644
index 3b5e64b..0000000
--- a/myenv/Lib/site-packages/blinker-1.8.2.dist-info/WHEEL
+++ /dev/null
@@ -1,4 +0,0 @@
-Wheel-Version: 1.0
-Generator: flit 3.9.0
-Root-Is-Purelib: true
-Tag: py3-none-any
diff --git a/myenv/Lib/site-packages/blinker/__init__.py b/myenv/Lib/site-packages/blinker/__init__.py
deleted file mode 100644
index c93527e..0000000
--- a/myenv/Lib/site-packages/blinker/__init__.py
+++ /dev/null
@@ -1,60 +0,0 @@
-from __future__ import annotations
-
-import typing as t
-
-from .base import ANY
-from .base import default_namespace
-from .base import NamedSignal
-from .base import Namespace
-from .base import Signal
-from .base import signal
-
-__all__ = [
- "ANY",
- "default_namespace",
- "NamedSignal",
- "Namespace",
- "Signal",
- "signal",
-]
-
-
-def __getattr__(name: str) -> t.Any:
- import warnings
-
- if name == "__version__":
- import importlib.metadata
-
- warnings.warn(
- "The '__version__' attribute is deprecated and will be removed in"
- " Blinker 1.9.0. Use feature detection or"
- " 'importlib.metadata.version(\"blinker\")' instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return importlib.metadata.version("blinker")
-
- if name == "receiver_connected":
- from .base import _receiver_connected
-
- warnings.warn(
- "The global 'receiver_connected' signal is deprecated and will be"
- " removed in Blinker 1.9. Use 'Signal.receiver_connected' and"
- " 'Signal.receiver_disconnected' instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return _receiver_connected
-
- if name == "WeakNamespace":
- from .base import _WeakNamespace
-
- warnings.warn(
- "'WeakNamespace' is deprecated and will be removed in Blinker 1.9."
- " Use 'Namespace' instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return _WeakNamespace
-
- raise AttributeError(name)
diff --git a/myenv/Lib/site-packages/blinker/__pycache__/__init__.cpython-38.pyc b/myenv/Lib/site-packages/blinker/__pycache__/__init__.cpython-38.pyc
deleted file mode 100644
index 3e67a07..0000000
Binary files a/myenv/Lib/site-packages/blinker/__pycache__/__init__.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/blinker/__pycache__/_utilities.cpython-38.pyc b/myenv/Lib/site-packages/blinker/__pycache__/_utilities.cpython-38.pyc
deleted file mode 100644
index c8e5cbe..0000000
Binary files a/myenv/Lib/site-packages/blinker/__pycache__/_utilities.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/blinker/__pycache__/base.cpython-38.pyc b/myenv/Lib/site-packages/blinker/__pycache__/base.cpython-38.pyc
deleted file mode 100644
index cc8d16f..0000000
Binary files a/myenv/Lib/site-packages/blinker/__pycache__/base.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/blinker/_utilities.py b/myenv/Lib/site-packages/blinker/_utilities.py
deleted file mode 100644
index 000c902..0000000
--- a/myenv/Lib/site-packages/blinker/_utilities.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from __future__ import annotations
-
-import collections.abc as c
-import inspect
-import typing as t
-from weakref import ref
-from weakref import WeakMethod
-
-T = t.TypeVar("T")
-
-
-class Symbol:
- """A constant symbol, nicer than ``object()``. Repeated calls return the
- same instance.
-
- >>> Symbol('foo') is Symbol('foo')
- True
- >>> Symbol('foo')
- foo
- """
-
- symbols: t.ClassVar[dict[str, Symbol]] = {}
-
- def __new__(cls, name: str) -> Symbol:
- if name in cls.symbols:
- return cls.symbols[name]
-
- obj = super().__new__(cls)
- cls.symbols[name] = obj
- return obj
-
- def __init__(self, name: str) -> None:
- self.name = name
-
- def __repr__(self) -> str:
- return self.name
-
- def __getnewargs__(self) -> tuple[t.Any, ...]:
- return (self.name,)
-
-
-def make_id(obj: object) -> c.Hashable:
- """Get a stable identifier for a receiver or sender, to be used as a dict
- key or in a set.
- """
- if inspect.ismethod(obj):
- # The id of a bound method is not stable, but the id of the unbound
- # function and instance are.
- return id(obj.__func__), id(obj.__self__)
-
- if isinstance(obj, (str, int)):
- # Instances with the same value always compare equal and have the same
- # hash, even if the id may change.
- return obj
-
- # Assume other types are not hashable but will always be the same instance.
- return id(obj)
-
-
-def make_ref(obj: T, callback: c.Callable[[ref[T]], None] | None = None) -> ref[T]:
- if inspect.ismethod(obj):
- return WeakMethod(obj, callback) # type: ignore[arg-type, return-value]
-
- return ref(obj, callback)
diff --git a/myenv/Lib/site-packages/blinker/base.py b/myenv/Lib/site-packages/blinker/base.py
deleted file mode 100644
index ec494b1..0000000
--- a/myenv/Lib/site-packages/blinker/base.py
+++ /dev/null
@@ -1,621 +0,0 @@
-from __future__ import annotations
-
-import collections.abc as c
-import typing as t
-import warnings
-import weakref
-from collections import defaultdict
-from contextlib import AbstractContextManager
-from contextlib import contextmanager
-from functools import cached_property
-from inspect import iscoroutinefunction
-from weakref import WeakValueDictionary
-
-from ._utilities import make_id
-from ._utilities import make_ref
-from ._utilities import Symbol
-
-if t.TYPE_CHECKING:
- F = t.TypeVar("F", bound=c.Callable[..., t.Any])
-
-ANY = Symbol("ANY")
-"""Symbol for "any sender"."""
-
-ANY_ID = 0
-
-
-class Signal:
- """A notification emitter.
-
- :param doc: The docstring for the signal.
- """
-
- ANY = ANY
- """An alias for the :data:`~blinker.ANY` sender symbol."""
-
- set_class: type[set[t.Any]] = set
- """The set class to use for tracking connected receivers and senders.
- Python's ``set`` is unordered. If receivers must be dispatched in the order
- they were connected, an ordered set implementation can be used.
-
- .. versionadded:: 1.7
- """
-
- @cached_property
- def receiver_connected(self) -> Signal:
- """Emitted at the end of each :meth:`connect` call.
-
- The signal sender is the signal instance, and the :meth:`connect`
- arguments are passed through: ``receiver``, ``sender``, and ``weak``.
-
- .. versionadded:: 1.2
- """
- return Signal(doc="Emitted after a receiver connects.")
-
- @cached_property
- def receiver_disconnected(self) -> Signal:
- """Emitted at the end of each :meth:`disconnect` call.
-
- The sender is the signal instance, and the :meth:`disconnect` arguments
- are passed through: ``receiver`` and ``sender``.
-
- This signal is emitted **only** when :meth:`disconnect` is called
- explicitly. This signal cannot be emitted by an automatic disconnect
- when a weakly referenced receiver or sender goes out of scope, as the
- instance is no longer be available to be used as the sender for this
- signal.
-
- An alternative approach is available by subscribing to
- :attr:`receiver_connected` and setting up a custom weakref cleanup
- callback on weak receivers and senders.
-
- .. versionadded:: 1.2
- """
- return Signal(doc="Emitted after a receiver disconnects.")
-
- def __init__(self, doc: str | None = None) -> None:
- if doc:
- self.__doc__ = doc
-
- self.receivers: dict[
- t.Any, weakref.ref[c.Callable[..., t.Any]] | c.Callable[..., t.Any]
- ] = {}
- """The map of connected receivers. Useful to quickly check if any
- receivers are connected to the signal: ``if s.receivers:``. The
- structure and data is not part of the public API, but checking its
- boolean value is.
- """
-
- self.is_muted: bool = False
- self._by_receiver: dict[t.Any, set[t.Any]] = defaultdict(self.set_class)
- self._by_sender: dict[t.Any, set[t.Any]] = defaultdict(self.set_class)
- self._weak_senders: dict[t.Any, weakref.ref[t.Any]] = {}
-
- def connect(self, receiver: F, sender: t.Any = ANY, weak: bool = True) -> F:
- """Connect ``receiver`` to be called when the signal is sent by
- ``sender``.
-
- :param receiver: The callable to call when :meth:`send` is called with
- the given ``sender``, passing ``sender`` as a positional argument
- along with any extra keyword arguments.
- :param sender: Any object or :data:`ANY`. ``receiver`` will only be
- called when :meth:`send` is called with this sender. If ``ANY``, the
- receiver will be called for any sender. A receiver may be connected
- to multiple senders by calling :meth:`connect` multiple times.
- :param weak: Track the receiver with a :mod:`weakref`. The receiver will
- be automatically disconnected when it is garbage collected. When
- connecting a receiver defined within a function, set to ``False``,
- otherwise it will be disconnected when the function scope ends.
- """
- receiver_id = make_id(receiver)
- sender_id = ANY_ID if sender is ANY else make_id(sender)
-
- if weak:
- self.receivers[receiver_id] = make_ref(
- receiver, self._make_cleanup_receiver(receiver_id)
- )
- else:
- self.receivers[receiver_id] = receiver
-
- self._by_sender[sender_id].add(receiver_id)
- self._by_receiver[receiver_id].add(sender_id)
-
- if sender is not ANY and sender_id not in self._weak_senders:
- # store a cleanup for weakref-able senders
- try:
- self._weak_senders[sender_id] = make_ref(
- sender, self._make_cleanup_sender(sender_id)
- )
- except TypeError:
- pass
-
- if "receiver_connected" in self.__dict__ and self.receiver_connected.receivers:
- try:
- self.receiver_connected.send(
- self, receiver=receiver, sender=sender, weak=weak
- )
- except TypeError:
- # TODO no explanation or test for this
- self.disconnect(receiver, sender)
- raise
-
- if _receiver_connected.receivers and self is not _receiver_connected:
- try:
- _receiver_connected.send(
- self, receiver_arg=receiver, sender_arg=sender, weak_arg=weak
- )
- except TypeError:
- self.disconnect(receiver, sender)
- raise
-
- return receiver
-
- def connect_via(self, sender: t.Any, weak: bool = False) -> c.Callable[[F], F]:
- """Connect the decorated function to be called when the signal is sent
- by ``sender``.
-
- The decorated function will be called when :meth:`send` is called with
- the given ``sender``, passing ``sender`` as a positional argument along
- with any extra keyword arguments.
-
- :param sender: Any object or :data:`ANY`. ``receiver`` will only be
- called when :meth:`send` is called with this sender. If ``ANY``, the
- receiver will be called for any sender. A receiver may be connected
- to multiple senders by calling :meth:`connect` multiple times.
- :param weak: Track the receiver with a :mod:`weakref`. The receiver will
- be automatically disconnected when it is garbage collected. When
- connecting a receiver defined within a function, set to ``False``,
- otherwise it will be disconnected when the function scope ends.=
-
- .. versionadded:: 1.1
- """
-
- def decorator(fn: F) -> F:
- self.connect(fn, sender, weak)
- return fn
-
- return decorator
-
- @contextmanager
- def connected_to(
- self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY
- ) -> c.Generator[None, None, None]:
- """A context manager that temporarily connects ``receiver`` to the
- signal while a ``with`` block executes. When the block exits, the
- receiver is disconnected. Useful for tests.
-
- :param receiver: The callable to call when :meth:`send` is called with
- the given ``sender``, passing ``sender`` as a positional argument
- along with any extra keyword arguments.
- :param sender: Any object or :data:`ANY`. ``receiver`` will only be
- called when :meth:`send` is called with this sender. If ``ANY``, the
- receiver will be called for any sender.
-
- .. versionadded:: 1.1
- """
- self.connect(receiver, sender=sender, weak=False)
-
- try:
- yield None
- finally:
- self.disconnect(receiver)
-
- @contextmanager
- def muted(self) -> c.Generator[None, None, None]:
- """A context manager that temporarily disables the signal. No receivers
- will be called if the signal is sent, until the ``with`` block exits.
- Useful for tests.
- """
- self.is_muted = True
-
- try:
- yield None
- finally:
- self.is_muted = False
-
- def temporarily_connected_to(
- self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY
- ) -> AbstractContextManager[None]:
- """Deprecated alias for :meth:`connected_to`.
-
- .. deprecated:: 1.1
- Renamed to ``connected_to``. Will be removed in Blinker 1.9.
-
- .. versionadded:: 0.9
- """
- warnings.warn(
- "'temporarily_connected_to' is renamed to 'connected_to'. The old name is"
- " deprecated and will be removed in Blinker 1.9.",
- DeprecationWarning,
- stacklevel=2,
- )
- return self.connected_to(receiver, sender)
-
- def send(
- self,
- sender: t.Any | None = None,
- /,
- *,
- _async_wrapper: c.Callable[
- [c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]]], c.Callable[..., t.Any]
- ]
- | None = None,
- **kwargs: t.Any,
- ) -> list[tuple[c.Callable[..., t.Any], t.Any]]:
- """Call all receivers that are connected to the given ``sender``
- or :data:`ANY`. Each receiver is called with ``sender`` as a positional
- argument along with any extra keyword arguments. Return a list of
- ``(receiver, return value)`` tuples.
-
- The order receivers are called is undefined, but can be influenced by
- setting :attr:`set_class`.
-
- If a receiver raises an exception, that exception will propagate up.
- This makes debugging straightforward, with an assumption that correctly
- implemented receivers will not raise.
-
- :param sender: Call receivers connected to this sender, in addition to
- those connected to :data:`ANY`.
- :param _async_wrapper: Will be called on any receivers that are async
- coroutines to turn them into sync callables. For example, could run
- the receiver with an event loop.
- :param kwargs: Extra keyword arguments to pass to each receiver.
-
- .. versionchanged:: 1.7
- Added the ``_async_wrapper`` argument.
- """
- if self.is_muted:
- return []
-
- results = []
-
- for receiver in self.receivers_for(sender):
- if iscoroutinefunction(receiver):
- if _async_wrapper is None:
- raise RuntimeError("Cannot send to a coroutine function.")
-
- result = _async_wrapper(receiver)(sender, **kwargs)
- else:
- result = receiver(sender, **kwargs)
-
- results.append((receiver, result))
-
- return results
-
- async def send_async(
- self,
- sender: t.Any | None = None,
- /,
- *,
- _sync_wrapper: c.Callable[
- [c.Callable[..., t.Any]], c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]]
- ]
- | None = None,
- **kwargs: t.Any,
- ) -> list[tuple[c.Callable[..., t.Any], t.Any]]:
- """Await all receivers that are connected to the given ``sender``
- or :data:`ANY`. Each receiver is called with ``sender`` as a positional
- argument along with any extra keyword arguments. Return a list of
- ``(receiver, return value)`` tuples.
-
- The order receivers are called is undefined, but can be influenced by
- setting :attr:`set_class`.
-
- If a receiver raises an exception, that exception will propagate up.
- This makes debugging straightforward, with an assumption that correctly
- implemented receivers will not raise.
-
- :param sender: Call receivers connected to this sender, in addition to
- those connected to :data:`ANY`.
- :param _sync_wrapper: Will be called on any receivers that are sync
- callables to turn them into async coroutines. For example,
- could call the receiver in a thread.
- :param kwargs: Extra keyword arguments to pass to each receiver.
-
- .. versionadded:: 1.7
- """
- if self.is_muted:
- return []
-
- results = []
-
- for receiver in self.receivers_for(sender):
- if not iscoroutinefunction(receiver):
- if _sync_wrapper is None:
- raise RuntimeError("Cannot send to a non-coroutine function.")
-
- result = await _sync_wrapper(receiver)(sender, **kwargs)
- else:
- result = await receiver(sender, **kwargs)
-
- results.append((receiver, result))
-
- return results
-
- def has_receivers_for(self, sender: t.Any) -> bool:
- """Check if there is at least one receiver that will be called with the
- given ``sender``. A receiver connected to :data:`ANY` will always be
- called, regardless of sender. Does not check if weakly referenced
- receivers are still live. See :meth:`receivers_for` for a stronger
- search.
-
- :param sender: Check for receivers connected to this sender, in addition
- to those connected to :data:`ANY`.
- """
- if not self.receivers:
- return False
-
- if self._by_sender[ANY_ID]:
- return True
-
- if sender is ANY:
- return False
-
- return make_id(sender) in self._by_sender
-
- def receivers_for(
- self, sender: t.Any
- ) -> c.Generator[c.Callable[..., t.Any], None, None]:
- """Yield each receiver to be called for ``sender``, in addition to those
- to be called for :data:`ANY`. Weakly referenced receivers that are not
- live will be disconnected and skipped.
-
- :param sender: Yield receivers connected to this sender, in addition
- to those connected to :data:`ANY`.
- """
- # TODO: test receivers_for(ANY)
- if not self.receivers:
- return
-
- sender_id = make_id(sender)
-
- if sender_id in self._by_sender:
- ids = self._by_sender[ANY_ID] | self._by_sender[sender_id]
- else:
- ids = self._by_sender[ANY_ID].copy()
-
- for receiver_id in ids:
- receiver = self.receivers.get(receiver_id)
-
- if receiver is None:
- continue
-
- if isinstance(receiver, weakref.ref):
- strong = receiver()
-
- if strong is None:
- self._disconnect(receiver_id, ANY_ID)
- continue
-
- yield strong
- else:
- yield receiver
-
- def disconnect(self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY) -> None:
- """Disconnect ``receiver`` from being called when the signal is sent by
- ``sender``.
-
- :param receiver: A connected receiver callable.
- :param sender: Disconnect from only this sender. By default, disconnect
- from all senders.
- """
- sender_id: c.Hashable
-
- if sender is ANY:
- sender_id = ANY_ID
- else:
- sender_id = make_id(sender)
-
- receiver_id = make_id(receiver)
- self._disconnect(receiver_id, sender_id)
-
- if (
- "receiver_disconnected" in self.__dict__
- and self.receiver_disconnected.receivers
- ):
- self.receiver_disconnected.send(self, receiver=receiver, sender=sender)
-
- def _disconnect(self, receiver_id: c.Hashable, sender_id: c.Hashable) -> None:
- if sender_id == ANY_ID:
- if self._by_receiver.pop(receiver_id, None) is not None:
- for bucket in self._by_sender.values():
- bucket.discard(receiver_id)
-
- self.receivers.pop(receiver_id, None)
- else:
- self._by_sender[sender_id].discard(receiver_id)
- self._by_receiver[receiver_id].discard(sender_id)
-
- def _make_cleanup_receiver(
- self, receiver_id: c.Hashable
- ) -> c.Callable[[weakref.ref[c.Callable[..., t.Any]]], None]:
- """Create a callback function to disconnect a weakly referenced
- receiver when it is garbage collected.
- """
-
- def cleanup(ref: weakref.ref[c.Callable[..., t.Any]]) -> None:
- self._disconnect(receiver_id, ANY_ID)
-
- return cleanup
-
- def _make_cleanup_sender(
- self, sender_id: c.Hashable
- ) -> c.Callable[[weakref.ref[t.Any]], None]:
- """Create a callback function to disconnect all receivers for a weakly
- referenced sender when it is garbage collected.
- """
- assert sender_id != ANY_ID
-
- def cleanup(ref: weakref.ref[t.Any]) -> None:
- self._weak_senders.pop(sender_id, None)
-
- for receiver_id in self._by_sender.pop(sender_id, ()):
- self._by_receiver[receiver_id].discard(sender_id)
-
- return cleanup
-
- def _cleanup_bookkeeping(self) -> None:
- """Prune unused sender/receiver bookkeeping. Not threadsafe.
-
- Connecting & disconnecting leaves behind a small amount of bookkeeping
- data. Typical workloads using Blinker, for example in most web apps,
- Flask, CLI scripts, etc., are not adversely affected by this
- bookkeeping.
-
- With a long-running process performing dynamic signal routing with high
- volume, e.g. connecting to function closures, senders are all unique
- object instances. Doing all of this over and over may cause memory usage
- to grow due to extraneous bookkeeping. (An empty ``set`` for each stale
- sender/receiver pair.)
-
- This method will prune that bookkeeping away, with the caveat that such
- pruning is not threadsafe. The risk is that cleanup of a fully
- disconnected receiver/sender pair occurs while another thread is
- connecting that same pair. If you are in the highly dynamic, unique
- receiver/sender situation that has lead you to this method, that failure
- mode is perhaps not a big deal for you.
- """
- for mapping in (self._by_sender, self._by_receiver):
- for ident, bucket in list(mapping.items()):
- if not bucket:
- mapping.pop(ident, None)
-
- def _clear_state(self) -> None:
- """Disconnect all receivers and senders. Useful for tests."""
- self._weak_senders.clear()
- self.receivers.clear()
- self._by_sender.clear()
- self._by_receiver.clear()
-
-
-_receiver_connected = Signal(
- """\
-Sent by a :class:`Signal` after a receiver connects.
-
-:argument: the Signal that was connected to
-:keyword receiver_arg: the connected receiver
-:keyword sender_arg: the sender to connect to
-:keyword weak_arg: true if the connection to receiver_arg is a weak reference
-
-.. deprecated:: 1.2
- Individual signals have their own :attr:`~Signal.receiver_connected` and
- :attr:`~Signal.receiver_disconnected` signals with a slightly simplified
- call signature. This global signal will be removed in Blinker 1.9.
-"""
-)
-
-
-class NamedSignal(Signal):
- """A named generic notification emitter. The name is not used by the signal
- itself, but matches the key in the :class:`Namespace` that it belongs to.
-
- :param name: The name of the signal within the namespace.
- :param doc: The docstring for the signal.
- """
-
- def __init__(self, name: str, doc: str | None = None) -> None:
- super().__init__(doc)
-
- #: The name of this signal.
- self.name: str = name
-
- def __repr__(self) -> str:
- base = super().__repr__()
- return f"{base[:-1]}; {self.name!r}>" # noqa: E702
-
-
-if t.TYPE_CHECKING:
-
- class PNamespaceSignal(t.Protocol):
- def __call__(self, name: str, doc: str | None = None) -> NamedSignal: ...
-
- # Python < 3.9
- _NamespaceBase = dict[str, NamedSignal] # type: ignore[misc]
-else:
- _NamespaceBase = dict
-
-
-class Namespace(_NamespaceBase):
- """A dict mapping names to signals."""
-
- def signal(self, name: str, doc: str | None = None) -> NamedSignal:
- """Return the :class:`NamedSignal` for the given ``name``, creating it
- if required. Repeated calls with the same name return the same signal.
-
- :param name: The name of the signal.
- :param doc: The docstring of the signal.
- """
- if name not in self:
- self[name] = NamedSignal(name, doc)
-
- return self[name]
-
-
-class _WeakNamespace(WeakValueDictionary): # type: ignore[type-arg]
- """A weak mapping of names to signals.
-
- Automatically cleans up unused signals when the last reference goes out
- of scope. This namespace implementation provides similar behavior to Blinker
- <= 1.2.
-
- .. deprecated:: 1.3
- Will be removed in Blinker 1.9.
-
- .. versionadded:: 1.3
- """
-
- def __init__(self) -> None:
- warnings.warn(
- "'WeakNamespace' is deprecated and will be removed in Blinker 1.9."
- " Use 'Namespace' instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- super().__init__()
-
- def signal(self, name: str, doc: str | None = None) -> NamedSignal:
- """Return the :class:`NamedSignal` for the given ``name``, creating it
- if required. Repeated calls with the same name return the same signal.
-
- :param name: The name of the signal.
- :param doc: The docstring of the signal.
- """
- if name not in self:
- self[name] = NamedSignal(name, doc)
-
- return self[name] # type: ignore[no-any-return]
-
-
-default_namespace: Namespace = Namespace()
-"""A default :class:`Namespace` for creating named signals. :func:`signal`
-creates a :class:`NamedSignal` in this namespace.
-"""
-
-signal: PNamespaceSignal = default_namespace.signal
-"""Return a :class:`NamedSignal` in :data:`default_namespace` with the given
-``name``, creating it if required. Repeated calls with the same name return the
-same signal.
-"""
-
-
-def __getattr__(name: str) -> t.Any:
- if name == "receiver_connected":
- warnings.warn(
- "The global 'receiver_connected' signal is deprecated and will be"
- " removed in Blinker 1.9. Use 'Signal.receiver_connected' and"
- " 'Signal.receiver_disconnected' instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return _receiver_connected
-
- if name == "WeakNamespace":
- warnings.warn(
- "'WeakNamespace' is deprecated and will be removed in Blinker 1.9."
- " Use 'Namespace' instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return _WeakNamespace
-
- raise AttributeError(name)
diff --git a/myenv/Lib/site-packages/blinker/py.typed b/myenv/Lib/site-packages/blinker/py.typed
deleted file mode 100644
index e69de29..0000000
diff --git a/myenv/Lib/site-packages/bs4/__init__.py b/myenv/Lib/site-packages/bs4/__init__.py
deleted file mode 100644
index 19b7c0e..0000000
--- a/myenv/Lib/site-packages/bs4/__init__.py
+++ /dev/null
@@ -1,1174 +0,0 @@
-"""Beautiful Soup Elixir and Tonic - "The Screen-Scraper's Friend".
-
-http://www.crummy.com/software/BeautifulSoup/
-
-Beautiful Soup uses a pluggable XML or HTML parser to parse a
-(possibly invalid) document into a tree representation. Beautiful Soup
-provides methods and Pythonic idioms that make it easy to navigate,
-search, and modify the parse tree.
-
-Beautiful Soup works with Python 3.7 and up. It works better if lxml
-and/or html5lib is installed, but they are not required.
-
-For more than you ever wanted to know about Beautiful Soup, see the
-documentation: http://www.crummy.com/software/BeautifulSoup/bs4/doc/
-"""
-
-__author__ = "Leonard Richardson (leonardr@segfault.org)"
-__version__ = "4.14.3"
-__copyright__ = "Copyright (c) 2004-2025 Leonard Richardson"
-# Use of this source code is governed by the MIT license.
-__license__ = "MIT"
-
-__all__ = [
- "AttributeResemblesVariableWarning",
- "BeautifulSoup",
- "Comment",
- "Declaration",
- "ProcessingInstruction",
- "ResultSet",
- "CSS",
- "Script",
- "Stylesheet",
- "Tag",
- "TemplateString",
- "ElementFilter",
- "UnicodeDammit",
- "CData",
- "Doctype",
-
- # Exceptions
- "FeatureNotFound",
- "ParserRejectedMarkup",
- "StopParsing",
-
- # Warnings
- "AttributeResemblesVariableWarning",
- "GuessedAtParserWarning",
- "MarkupResemblesLocatorWarning",
- "UnusualUsageWarning",
- "XMLParsedAsHTMLWarning",
-]
-
-from collections import Counter
-import io
-import sys
-import warnings
-
-# The very first thing we do is give a useful error if someone is
-# running this code under Python 2.
-if sys.version_info.major < 3:
- raise ImportError(
- "You are trying to use a Python 3-specific version of Beautiful Soup under Python 2. This will not work. The final version of Beautiful Soup to support Python 2 was 4.9.3."
- )
-
-from .builder import (
- builder_registry,
- TreeBuilder,
-)
-from .builder._htmlparser import HTMLParserTreeBuilder
-from .dammit import UnicodeDammit
-from .css import CSS
-from ._deprecation import (
- _deprecated,
-)
-from .element import (
- CData,
- Comment,
- DEFAULT_OUTPUT_ENCODING,
- Declaration,
- Doctype,
- NavigableString,
- PageElement,
- ProcessingInstruction,
- PYTHON_SPECIFIC_ENCODINGS,
- ResultSet,
- Script,
- Stylesheet,
- Tag,
- TemplateString,
-)
-from .formatter import Formatter
-from .filter import (
- ElementFilter,
- SoupStrainer,
-)
-from typing import (
- Any,
- cast,
- Counter as CounterType,
- Dict,
- Iterator,
- List,
- Sequence,
- Sized,
- Optional,
- Type,
- Union,
-)
-
-from bs4._typing import (
- _Encoding,
- _Encodings,
- _IncomingMarkup,
- _InsertableElement,
- _RawAttributeValue,
- _RawAttributeValues,
- _RawMarkup,
-)
-
-# Import all warnings and exceptions into the main package.
-from bs4.exceptions import (
- FeatureNotFound,
- ParserRejectedMarkup,
- StopParsing,
-)
-from bs4._warnings import (
- AttributeResemblesVariableWarning,
- GuessedAtParserWarning,
- MarkupResemblesLocatorWarning,
- UnusualUsageWarning,
- XMLParsedAsHTMLWarning,
-)
-
-
-class BeautifulSoup(Tag):
- """A data structure representing a parsed HTML or XML document.
-
- Most of the methods you'll call on a BeautifulSoup object are inherited from
- PageElement or Tag.
-
- Internally, this class defines the basic interface called by the
- tree builders when converting an HTML/XML document into a data
- structure. The interface abstracts away the differences between
- parsers. To write a new tree builder, you'll need to understand
- these methods as a whole.
-
- These methods will be called by the BeautifulSoup constructor:
- * reset()
- * feed(markup)
-
- The tree builder may call these methods from its feed() implementation:
- * handle_starttag(name, attrs) # See note about return value
- * handle_endtag(name)
- * handle_data(data) # Appends to the current data node
- * endData(containerClass) # Ends the current data node
-
- No matter how complicated the underlying parser is, you should be
- able to build a tree using 'start tag' events, 'end tag' events,
- 'data' events, and "done with data" events.
-
- If you encounter an empty-element tag (aka a self-closing tag,
- like HTML's tag), call handle_starttag and then
- handle_endtag.
- """
-
- #: Since `BeautifulSoup` subclasses `Tag`, it's possible to treat it as
- #: a `Tag` with a `Tag.name`. Hoever, this name makes it clear the
- #: `BeautifulSoup` object isn't a real markup tag.
- ROOT_TAG_NAME: str = "[document]"
-
- #: If the end-user gives no indication which tree builder they
- #: want, look for one with these features.
- DEFAULT_BUILDER_FEATURES: Sequence[str] = ["html", "fast"]
-
- #: A string containing all ASCII whitespace characters, used in
- #: during parsing to detect data chunks that seem 'empty'.
- ASCII_SPACES: str = "\x20\x0a\x09\x0c\x0d"
-
- # FUTURE PYTHON:
- element_classes: Dict[Type[PageElement], Type[PageElement]] #: :meta private:
- builder: TreeBuilder #: :meta private:
- is_xml: bool
- known_xml: Optional[bool]
- parse_only: Optional[SoupStrainer] #: :meta private:
-
- # These members are only used while parsing markup.
- markup: Optional[_RawMarkup] #: :meta private:
- current_data: List[str] #: :meta private:
- currentTag: Optional[Tag] #: :meta private:
- tagStack: List[Tag] #: :meta private:
- open_tag_counter: CounterType[str] #: :meta private:
- preserve_whitespace_tag_stack: List[Tag] #: :meta private:
- string_container_stack: List[Tag] #: :meta private:
- _most_recent_element: Optional[PageElement] #: :meta private:
-
- #: Beautiful Soup's best guess as to the character encoding of the
- #: original document.
- original_encoding: Optional[_Encoding]
-
- #: The character encoding, if any, that was explicitly defined
- #: in the original document. This may or may not match
- #: `BeautifulSoup.original_encoding`.
- declared_html_encoding: Optional[_Encoding]
-
- #: This is True if the markup that was parsed contains
- #: U+FFFD REPLACEMENT_CHARACTER characters which were not present
- #: in the original markup. These mark character sequences that
- #: could not be represented in Unicode.
- contains_replacement_characters: bool
-
- def __init__(
- self,
- markup: _IncomingMarkup = "",
- features: Optional[Union[str, Sequence[str]]] = None,
- builder: Optional[Union[TreeBuilder, Type[TreeBuilder]]] = None,
- parse_only: Optional[SoupStrainer] = None,
- from_encoding: Optional[_Encoding] = None,
- exclude_encodings: Optional[_Encodings] = None,
- element_classes: Optional[Dict[Type[PageElement], Type[PageElement]]] = None,
- **kwargs: Any,
- ):
- """Constructor.
-
- :param markup: A string or a file-like object representing
- markup to be parsed.
-
- :param features: Desirable features of the parser to be
- used. This may be the name of a specific parser ("lxml",
- "lxml-xml", "html.parser", or "html5lib") or it may be the
- type of markup to be used ("html", "html5", "xml"). It's
- recommended that you name a specific parser, so that
- Beautiful Soup gives you the same results across platforms
- and virtual environments.
-
- :param builder: A TreeBuilder subclass to instantiate (or
- instance to use) instead of looking one up based on
- `features`. You only need to use this if you've implemented a
- custom TreeBuilder.
-
- :param parse_only: A SoupStrainer. Only parts of the document
- matching the SoupStrainer will be considered. This is useful
- when parsing part of a document that would otherwise be too
- large to fit into memory.
-
- :param from_encoding: A string indicating the encoding of the
- document to be parsed. Pass this in if Beautiful Soup is
- guessing wrongly about the document's encoding.
-
- :param exclude_encodings: A list of strings indicating
- encodings known to be wrong. Pass this in if you don't know
- the document's encoding but you know Beautiful Soup's guess is
- wrong.
-
- :param element_classes: A dictionary mapping BeautifulSoup
- classes like Tag and NavigableString, to other classes you'd
- like to be instantiated instead as the parse tree is
- built. This is useful for subclassing Tag or NavigableString
- to modify default behavior.
-
- :param kwargs: For backwards compatibility purposes, the
- constructor accepts certain keyword arguments used in
- Beautiful Soup 3. None of these arguments do anything in
- Beautiful Soup 4; they will result in a warning and then be
- ignored.
-
- Apart from this, any keyword arguments passed into the
- BeautifulSoup constructor are propagated to the TreeBuilder
- constructor. This makes it possible to configure a
- TreeBuilder by passing in arguments, not just by saying which
- one to use.
- """
- if "convertEntities" in kwargs:
- del kwargs["convertEntities"]
- warnings.warn(
- "BS4 does not respect the convertEntities argument to the "
- "BeautifulSoup constructor. Entities are always converted "
- "to Unicode characters."
- )
-
- if "markupMassage" in kwargs:
- del kwargs["markupMassage"]
- warnings.warn(
- "BS4 does not respect the markupMassage argument to the "
- "BeautifulSoup constructor. The tree builder is responsible "
- "for any necessary markup massage."
- )
-
- if "smartQuotesTo" in kwargs:
- del kwargs["smartQuotesTo"]
- warnings.warn(
- "BS4 does not respect the smartQuotesTo argument to the "
- "BeautifulSoup constructor. Smart quotes are always converted "
- "to Unicode characters."
- )
-
- if "selfClosingTags" in kwargs:
- del kwargs["selfClosingTags"]
- warnings.warn(
- "Beautiful Soup 4 does not respect the selfClosingTags argument to the "
- "BeautifulSoup constructor. The tree builder is responsible "
- "for understanding self-closing tags."
- )
-
- if "isHTML" in kwargs:
- del kwargs["isHTML"]
- warnings.warn(
- "Beautiful Soup 4 does not respect the isHTML argument to the "
- "BeautifulSoup constructor. Suggest you use "
- "features='lxml' for HTML and features='lxml-xml' for "
- "XML."
- )
-
- def deprecated_argument(old_name: str, new_name: str) -> Optional[Any]:
- if old_name in kwargs:
- warnings.warn(
- 'The "%s" argument to the BeautifulSoup constructor '
- 'was renamed to "%s" in Beautiful Soup 4.0.0'
- % (old_name, new_name),
- DeprecationWarning,
- stacklevel=3,
- )
- return kwargs.pop(old_name)
- return None
-
- parse_only = parse_only or deprecated_argument("parseOnlyThese", "parse_only")
- if parse_only is not None:
- # Issue a warning if we can tell in advance that
- # parse_only will exclude the entire tree.
- if parse_only.excludes_everything:
- warnings.warn(
- f"The given value for parse_only will exclude everything: {parse_only}",
- UserWarning,
- stacklevel=3,
- )
-
- from_encoding = from_encoding or deprecated_argument(
- "fromEncoding", "from_encoding"
- )
-
- if from_encoding and isinstance(markup, str):
- warnings.warn(
- "You provided Unicode markup but also provided a value for from_encoding. Your from_encoding will be ignored."
- )
- from_encoding = None
-
- self.element_classes = element_classes or dict()
-
- # We need this information to track whether or not the builder
- # was specified well enough that we can omit the 'you need to
- # specify a parser' warning.
- original_builder = builder
- original_features = features
-
- builder_class: Optional[Type[TreeBuilder]] = None
- if isinstance(builder, type):
- # A builder class was passed in; it needs to be instantiated.
- builder_class = builder
- builder = None
- elif builder is None:
- if isinstance(features, str):
- features = [features]
- if features is None or len(features) == 0:
- features = self.DEFAULT_BUILDER_FEATURES
- possible_builder_class = builder_registry.lookup(*features)
- if possible_builder_class is None:
- raise FeatureNotFound(
- "Couldn't find a tree builder with the features you "
- "requested: %s. Do you need to install a parser library?"
- % ",".join(features)
- )
- builder_class = possible_builder_class
-
- # At this point either we have a TreeBuilder instance in
- # builder, or we have a builder_class that we can instantiate
- # with the remaining **kwargs.
- if builder is None:
- assert builder_class is not None
- builder = builder_class(**kwargs)
- if (
- not original_builder
- and not (
- original_features == builder.NAME
- or (
- isinstance(original_features, str)
- and original_features in builder.ALTERNATE_NAMES
- )
- )
- and markup
- ):
- # The user did not tell us which TreeBuilder to use,
- # and we had to guess. Issue a warning.
- if builder.is_xml:
- markup_type = "XML"
- else:
- markup_type = "HTML"
-
- # This code adapted from warnings.py so that we get the same line
- # of code as our warnings.warn() call gets, even if the answer is wrong
- # (as it may be in a multithreading situation).
- caller = None
- try:
- caller = sys._getframe(1)
- except ValueError:
- pass
- if caller:
- globals = caller.f_globals
- line_number = caller.f_lineno
- else:
- globals = sys.__dict__
- line_number = 1
- filename = globals.get("__file__")
- if filename:
- fnl = filename.lower()
- if fnl.endswith((".pyc", ".pyo")):
- filename = filename[:-1]
- if filename:
- # If there is no filename at all, the user is most likely in a REPL,
- # and the warning is not necessary.
- values = dict(
- filename=filename,
- line_number=line_number,
- parser=builder.NAME,
- markup_type=markup_type,
- )
- warnings.warn(
- GuessedAtParserWarning.MESSAGE % values,
- GuessedAtParserWarning,
- stacklevel=2,
- )
- else:
- if kwargs:
- warnings.warn(
- "Keyword arguments to the BeautifulSoup constructor will be ignored. These would normally be passed into the TreeBuilder constructor, but a TreeBuilder instance was passed in as `builder`."
- )
-
- self.builder = builder
- self.is_xml = builder.is_xml
- self.known_xml = self.is_xml
- self._namespaces = dict()
- self.parse_only = parse_only
-
- if hasattr(markup, "read"): # It's a file-type object.
- markup = cast(io.IOBase, markup).read()
- elif not isinstance(markup, (bytes, str)) and not hasattr(markup, "__len__"):
- raise TypeError(
- f"Incoming markup is of an invalid type: {markup!r}. Markup must be a string, a bytestring, or an open filehandle."
- )
- elif isinstance(markup, Sized) and len(markup) <= 256 and (
- (isinstance(markup, bytes) and b"<" not in markup and b"\n" not in markup)
- or (isinstance(markup, str) and "<" not in markup and "\n" not in markup)
- ):
- # Issue warnings for a couple beginner problems
- # involving passing non-markup to Beautiful Soup.
- # Beautiful Soup will still parse the input as markup,
- # since that is sometimes the intended behavior.
- if not self._markup_is_url(markup):
- self._markup_resembles_filename(markup)
-
- # At this point we know markup is a string or bytestring. If
- # it was a file-type object, we've read from it.
- markup = cast(_RawMarkup, markup)
-
- rejections = []
- success = False
- for (
- self.markup,
- self.original_encoding,
- self.declared_html_encoding,
- self.contains_replacement_characters,
- ) in self.builder.prepare_markup(
- markup, from_encoding, exclude_encodings=exclude_encodings
- ):
- self.reset()
- self.builder.initialize_soup(self)
- try:
- self._feed()
- success = True
- break
- except ParserRejectedMarkup as e:
- rejections.append(e)
- pass
-
- if not success:
- other_exceptions = [str(e) for e in rejections]
- raise ParserRejectedMarkup(
- "The markup you provided was rejected by the parser. Trying a different parser or a different encoding may help.\n\nOriginal exception(s) from parser:\n "
- + "\n ".join(other_exceptions)
- )
-
- # Clear out the markup and remove the builder's circular
- # reference to this object.
- self.markup = None
- self.builder.soup = None
-
- def copy_self(self) -> "BeautifulSoup":
- """Create a new BeautifulSoup object with the same TreeBuilder,
- but not associated with any markup.
-
- This is the first step of the deepcopy process.
- """
- clone = type(self)("", None, self.builder)
-
- # Keep track of the encoding of the original document,
- # since we won't be parsing it again.
- clone.original_encoding = self.original_encoding
- return clone
-
- def __getstate__(self) -> Dict[str, Any]:
- # Frequently a tree builder can't be pickled.
- d = dict(self.__dict__)
- if "builder" in d and d["builder"] is not None and not self.builder.picklable:
- d["builder"] = type(self.builder)
- # Store the contents as a Unicode string.
- d["contents"] = []
- d["markup"] = self.decode()
-
- # If _most_recent_element is present, it's a Tag object left
- # over from initial parse. It might not be picklable and we
- # don't need it.
- if "_most_recent_element" in d:
- del d["_most_recent_element"]
- return d
-
- def __setstate__(self, state: Dict[str, Any]) -> None:
- # If necessary, restore the TreeBuilder by looking it up.
- self.__dict__ = state
- if isinstance(self.builder, type):
- self.builder = self.builder()
- elif not self.builder:
- # We don't know which builder was used to build this
- # parse tree, so use a default we know is always available.
- self.builder = HTMLParserTreeBuilder()
- self.builder.soup = self
- self.reset()
- self._feed()
-
- @classmethod
- @_deprecated(
- replaced_by="nothing (private method, will be removed)", version="4.13.0"
- )
- def _decode_markup(cls, markup: _RawMarkup) -> str:
- """Ensure `markup` is Unicode so it's safe to send into warnings.warn.
-
- warnings.warn had this problem back in 2010 but fortunately
- not anymore. This has not been used for a long time; I just
- noticed that fact while working on 4.13.0.
- """
- if isinstance(markup, bytes):
- decoded = markup.decode("utf-8", "replace")
- else:
- decoded = markup
- return decoded
-
- @classmethod
- def _markup_is_url(cls, markup: _RawMarkup) -> bool:
- """Error-handling method to raise a warning if incoming markup looks
- like a URL.
-
- :param markup: A string of markup.
- :return: Whether or not the markup resembled a URL
- closely enough to justify issuing a warning.
- """
- problem: bool = False
- if isinstance(markup, bytes):
- problem = (
- any(markup.startswith(prefix) for prefix in (b"http:", b"https:"))
- and b" " not in markup
- )
- elif isinstance(markup, str):
- problem = (
- any(markup.startswith(prefix) for prefix in ("http:", "https:"))
- and " " not in markup
- )
- else:
- return False
-
- if not problem:
- return False
- warnings.warn(
- MarkupResemblesLocatorWarning.URL_MESSAGE % dict(what="URL"),
- MarkupResemblesLocatorWarning,
- stacklevel=3,
- )
- return True
-
- @classmethod
- def _markup_resembles_filename(cls, markup: _RawMarkup) -> bool:
- """Error-handling method to issue a warning if incoming markup
- resembles a filename.
-
- :param markup: A string of markup.
- :return: Whether or not the markup resembled a filename
- closely enough to justify issuing a warning.
- """
- markup_b: bytes
-
- # We're only checking ASCII characters, so rather than write
- # the same tests twice, convert Unicode to a bytestring and
- # operate on the bytestring.
- if isinstance(markup, str):
- markup_b = markup.encode("utf8")
- else:
- markup_b = markup
-
- # Step 1: does it end with a common textual file extension?
- filelike = False
- lower = markup_b.lower()
- extensions = [b".html", b".htm", b".xml", b".xhtml", b".txt"]
- if any(lower.endswith(ext) for ext in extensions):
- filelike = True
- if not filelike:
- return False
-
- # Step 2: it _might_ be a file, but there are a few things
- # we can look for that aren't very common in filenames.
-
- # Characters that have special meaning to Unix shells. (< was
- # excluded before this method was called.)
- #
- # Many of these are also reserved characters that cannot
- # appear in Windows filenames.
- for byte in markup_b:
- if byte in b"?*#&;>$|":
- return False
-
- # Two consecutive forward slashes (as seen in a URL) or two
- # consecutive spaces (as seen in fixed-width data).
- #
- # (Paths to Windows network shares contain consecutive
- # backslashes, so checking that doesn't seem as helpful.)
- if b"//" in markup_b:
- return False
- if b" " in markup_b:
- return False
-
- # A colon in any position other than position 1 (e.g. after a
- # Windows drive letter).
- if markup_b.startswith(b":"):
- return False
- colon_i = markup_b.rfind(b":")
- if colon_i not in (-1, 1):
- return False
-
- # Step 3: If it survived all of those checks, it's similar
- # enough to a file to justify issuing a warning.
- warnings.warn(
- MarkupResemblesLocatorWarning.FILENAME_MESSAGE % dict(what="filename"),
- MarkupResemblesLocatorWarning,
- stacklevel=3,
- )
- return True
-
- def _feed(self) -> None:
- """Internal method that parses previously set markup, creating a large
- number of Tag and NavigableString objects.
- """
- # Convert the document to Unicode.
- self.builder.reset()
-
- if self.markup is not None:
- self.builder.feed(self.markup)
- # Close out any unfinished strings and close all the open tags.
- self.endData()
- while (
- self.currentTag is not None and self.currentTag.name != self.ROOT_TAG_NAME
- ):
- self.popTag()
-
- def reset(self) -> None:
- """Reset this object to a state as though it had never parsed any
- markup.
- """
- Tag.__init__(self, self, self.builder, self.ROOT_TAG_NAME)
- self.hidden = True
- self.builder.reset()
- self.current_data = []
- self.currentTag = None
- self.tagStack = []
- self.open_tag_counter = Counter()
- self.preserve_whitespace_tag_stack = []
- self.string_container_stack = []
- self._most_recent_element = None
- self.pushTag(self)
-
- def new_tag(
- self,
- name: str,
- namespace: Optional[str] = None,
- nsprefix: Optional[str] = None,
- attrs: Optional[_RawAttributeValues] = None,
- sourceline: Optional[int] = None,
- sourcepos: Optional[int] = None,
- string: Optional[str] = None,
- **kwattrs: _RawAttributeValue,
- ) -> Tag:
- """Create a new Tag associated with this BeautifulSoup object.
-
- :param name: The name of the new Tag.
- :param namespace: The URI of the new Tag's XML namespace, if any.
- :param prefix: The prefix for the new Tag's XML namespace, if any.
- :param attrs: A dictionary of this Tag's attribute values; can
- be used instead of ``kwattrs`` for attributes like 'class'
- that are reserved words in Python.
- :param sourceline: The line number where this tag was
- (purportedly) found in its source document.
- :param sourcepos: The character position within ``sourceline`` where this
- tag was (purportedly) found.
- :param string: String content for the new Tag, if any.
- :param kwattrs: Keyword arguments for the new Tag's attribute values.
-
- """
- attr_container = self.builder.attribute_dict_class(**kwattrs)
- if attrs is not None:
- attr_container.update(attrs)
- tag_class = self.element_classes.get(Tag, Tag)
-
- # Assume that this is either Tag or a subclass of Tag. If not,
- # the user brought type-unsafety upon themselves.
- tag_class = cast(Type[Tag], tag_class)
- tag = tag_class(
- None,
- self.builder,
- name,
- namespace,
- nsprefix,
- attr_container,
- sourceline=sourceline,
- sourcepos=sourcepos,
- )
-
- if string is not None:
- tag.string = string
- return tag
-
- def string_container(
- self, base_class: Optional[Type[NavigableString]] = None
- ) -> Type[NavigableString]:
- """Find the class that should be instantiated to hold a given kind of
- string.
-
- This may be a built-in Beautiful Soup class or a custom class passed
- in to the BeautifulSoup constructor.
- """
- container = base_class or NavigableString
-
- # The user may want us to use some other class (hopefully a
- # custom subclass) instead of the one we'd use normally.
- container = cast(
- Type[NavigableString], self.element_classes.get(container, container)
- )
-
- # On top of that, we may be inside a tag that needs a special
- # container class.
- if self.string_container_stack and container is NavigableString:
- container = self.builder.string_containers.get(
- self.string_container_stack[-1].name, container
- )
- return container
-
- def new_string(
- self, s: str, subclass: Optional[Type[NavigableString]] = None
- ) -> NavigableString:
- """Create a new `NavigableString` associated with this `BeautifulSoup`
- object.
-
- :param s: The string content of the `NavigableString`
- :param subclass: The subclass of `NavigableString`, if any, to
- use. If a document is being processed, an appropriate
- subclass for the current location in the document will
- be determined automatically.
- """
- container = self.string_container(subclass)
- return container(s)
-
- def insert_before(self, *args: _InsertableElement) -> List[PageElement]:
- """This method is part of the PageElement API, but `BeautifulSoup` doesn't implement
- it because there is nothing before or after it in the parse tree.
- """
- raise NotImplementedError(
- "BeautifulSoup objects don't support insert_before()."
- )
-
- def insert_after(self, *args: _InsertableElement) -> List[PageElement]:
- """This method is part of the PageElement API, but `BeautifulSoup` doesn't implement
- it because there is nothing before or after it in the parse tree.
- """
- raise NotImplementedError("BeautifulSoup objects don't support insert_after().")
-
- def popTag(self) -> Optional[Tag]:
- """Internal method called by _popToTag when a tag is closed.
-
- :meta private:
- """
- if not self.tagStack:
- # Nothing to pop. This shouldn't happen.
- return None
- tag = self.tagStack.pop()
- if tag.name in self.open_tag_counter:
- self.open_tag_counter[tag.name] -= 1
- if (
- self.preserve_whitespace_tag_stack
- and tag == self.preserve_whitespace_tag_stack[-1]
- ):
- self.preserve_whitespace_tag_stack.pop()
- if self.string_container_stack and tag == self.string_container_stack[-1]:
- self.string_container_stack.pop()
- # print("Pop", tag.name)
- if self.tagStack:
- self.currentTag = self.tagStack[-1]
- return self.currentTag
-
- def pushTag(self, tag: Tag) -> None:
- """Internal method called by handle_starttag when a tag is opened.
-
- :meta private:
- """
- # print("Push", tag.name)
- if self.currentTag is not None:
- self.currentTag.contents.append(tag)
- self.tagStack.append(tag)
- self.currentTag = self.tagStack[-1]
- if tag.name != self.ROOT_TAG_NAME:
- self.open_tag_counter[tag.name] += 1
- if tag.name in self.builder.preserve_whitespace_tags:
- self.preserve_whitespace_tag_stack.append(tag)
- if tag.name in self.builder.string_containers:
- self.string_container_stack.append(tag)
-
- def endData(self, containerClass: Optional[Type[NavigableString]] = None) -> None:
- """Method called by the TreeBuilder when the end of a data segment
- occurs.
-
- :param containerClass: The class to use when incorporating the
- data segment into the parse tree.
-
- :meta private:
- """
- if self.current_data:
- current_data = "".join(self.current_data)
- # If whitespace is not preserved, and this string contains
- # nothing but ASCII spaces, replace it with a single space
- # or newline.
- if not self.preserve_whitespace_tag_stack:
- strippable = True
- for i in current_data:
- if i not in self.ASCII_SPACES:
- strippable = False
- break
- if strippable:
- if "\n" in current_data:
- current_data = "\n"
- else:
- current_data = " "
-
- # Reset the data collector.
- self.current_data = []
-
- # Should we add this string to the tree at all?
- if (
- self.parse_only
- and len(self.tagStack) <= 1
- and (not self.parse_only.allow_string_creation(current_data))
- ):
- return
-
- containerClass = self.string_container(containerClass)
- o = containerClass(current_data)
- self.object_was_parsed(o)
-
- def object_was_parsed(
- self,
- o: PageElement,
- parent: Optional[Tag] = None,
- most_recent_element: Optional[PageElement] = None,
- ) -> None:
- """Method called by the TreeBuilder to integrate an object into the
- parse tree.
-
- :meta private:
- """
- if parent is None:
- parent = self.currentTag
- assert parent is not None
- previous_element: Optional[PageElement]
- if most_recent_element is not None:
- previous_element = most_recent_element
- else:
- previous_element = self._most_recent_element
-
- next_element = previous_sibling = next_sibling = None
- if isinstance(o, Tag):
- next_element = o.next_element
- next_sibling = o.next_sibling
- previous_sibling = o.previous_sibling
- if previous_element is None:
- previous_element = o.previous_element
-
- fix = parent.next_element is not None
-
- o.setup(parent, previous_element, next_element, previous_sibling, next_sibling)
-
- self._most_recent_element = o
- parent.contents.append(o)
-
- # Check if we are inserting into an already parsed node.
- if fix:
- self._linkage_fixer(parent)
-
- def _linkage_fixer(self, el: Tag) -> None:
- """Make sure linkage of this fragment is sound."""
-
- first = el.contents[0]
- child = el.contents[-1]
- descendant: PageElement = child
-
- if child is first and el.parent is not None:
- # Parent should be linked to first child
- el.next_element = child
- # We are no longer linked to whatever this element is
- prev_el = child.previous_element
- if prev_el is not None and prev_el is not el:
- prev_el.next_element = None
- # First child should be linked to the parent, and no previous siblings.
- child.previous_element = el
- child.previous_sibling = None
-
- # We have no sibling as we've been appended as the last.
- child.next_sibling = None
-
- # This index is a tag, dig deeper for a "last descendant"
- if isinstance(child, Tag) and child.contents:
- # _last_decendant is typed as returning Optional[PageElement],
- # but the value can't be None here, because el is a Tag
- # which we know has contents.
- descendant = cast(PageElement, child._last_descendant(False))
-
- # As the final step, link last descendant. It should be linked
- # to the parent's next sibling (if found), else walk up the chain
- # and find a parent with a sibling. It should have no next sibling.
- descendant.next_element = None
- descendant.next_sibling = None
-
- target: Optional[Tag] = el
- while True:
- if target is None:
- break
- elif target.next_sibling is not None:
- descendant.next_element = target.next_sibling
- target.next_sibling.previous_element = child
- break
- target = target.parent
-
- def _popToTag(
- self, name: str, nsprefix: Optional[str] = None, inclusivePop: bool = True
- ) -> Optional[Tag]:
- """Pops the tag stack up to and including the most recent
- instance of the given tag.
-
- If there are no open tags with the given name, nothing will be
- popped.
-
- :param name: Pop up to the most recent tag with this name.
- :param nsprefix: The namespace prefix that goes with `name`.
- :param inclusivePop: It this is false, pops the tag stack up
- to but *not* including the most recent instqance of the
- given tag.
-
- :meta private:
- """
- # print("Popping to %s" % name)
- if name == self.ROOT_TAG_NAME:
- # The BeautifulSoup object itself can never be popped.
- return None
-
- most_recently_popped = None
-
- stack_size = len(self.tagStack)
- for i in range(stack_size - 1, 0, -1):
- if not self.open_tag_counter.get(name):
- break
- t = self.tagStack[i]
- if name == t.name and nsprefix == t.prefix:
- if inclusivePop:
- most_recently_popped = self.popTag()
- break
- most_recently_popped = self.popTag()
-
- return most_recently_popped
-
- def handle_starttag(
- self,
- name: str,
- namespace: Optional[str],
- nsprefix: Optional[str],
- attrs: _RawAttributeValues,
- sourceline: Optional[int] = None,
- sourcepos: Optional[int] = None,
- namespaces: Optional[Dict[str, str]] = None,
- ) -> Optional[Tag]:
- """Called by the tree builder when a new tag is encountered.
-
- :param name: Name of the tag.
- :param nsprefix: Namespace prefix for the tag.
- :param attrs: A dictionary of attribute values. Note that
- attribute values are expected to be simple strings; processing
- of multi-valued attributes such as "class" comes later.
- :param sourceline: The line number where this tag was found in its
- source document.
- :param sourcepos: The character position within `sourceline` where this
- tag was found.
- :param namespaces: A dictionary of all namespace prefix mappings
- currently in scope in the document.
-
- If this method returns None, the tag was rejected by an active
- `ElementFilter`. You should proceed as if the tag had not occurred
- in the document. For instance, if this was a self-closing tag,
- don't call handle_endtag.
-
- :meta private:
- """
- # print("Start tag %s: %s" % (name, attrs))
- self.endData()
-
- if (
- self.parse_only
- and len(self.tagStack) <= 1
- and not self.parse_only.allow_tag_creation(nsprefix, name, attrs)
- ):
- return None
-
- tag_class = self.element_classes.get(Tag, Tag)
- # Assume that this is either Tag or a subclass of Tag. If not,
- # the user brought type-unsafety upon themselves.
- tag_class = cast(Type[Tag], tag_class)
- tag = tag_class(
- self,
- self.builder,
- name,
- namespace,
- nsprefix,
- attrs,
- self.currentTag,
- self._most_recent_element,
- sourceline=sourceline,
- sourcepos=sourcepos,
- namespaces=namespaces,
- )
- if tag is None:
- return tag
- if self._most_recent_element is not None:
- self._most_recent_element.next_element = tag
- self._most_recent_element = tag
- self.pushTag(tag)
- return tag
-
- def handle_endtag(self, name: str, nsprefix: Optional[str] = None) -> None:
- """Called by the tree builder when an ending tag is encountered.
-
- :param name: Name of the tag.
- :param nsprefix: Namespace prefix for the tag.
-
- :meta private:
- """
- # print("End tag: " + name)
- self.endData()
- self._popToTag(name, nsprefix)
-
- def handle_data(self, data: str) -> None:
- """Called by the tree builder when a chunk of textual data is
- encountered.
-
- :meta private:
- """
- self.current_data.append(data)
-
- def decode(
- self,
- indent_level: Optional[int] = None,
- eventual_encoding: _Encoding = DEFAULT_OUTPUT_ENCODING,
- formatter: Union[Formatter, str] = "minimal",
- iterator: Optional[Iterator[PageElement]] = None,
- **kwargs: Any,
- ) -> str:
- """Returns a string representation of the parse tree
- as a full HTML or XML document.
-
- :param indent_level: Each line of the rendering will be
- indented this many levels. (The ``formatter`` decides what a
- 'level' means, in terms of spaces or other characters
- output.) This is used internally in recursive calls while
- pretty-printing.
- :param eventual_encoding: The encoding of the final document.
- If this is None, the document will be a Unicode string.
- :param formatter: Either a `Formatter` object, or a string naming one of
- the standard formatters.
- :param iterator: The iterator to use when navigating over the
- parse tree. This is only used by `Tag.decode_contents` and
- you probably won't need to use it.
- """
- if self.is_xml:
- # Print the XML declaration
- encoding_part = ""
- declared_encoding: Optional[str] = eventual_encoding
- if eventual_encoding in PYTHON_SPECIFIC_ENCODINGS:
- # This is a special Python encoding; it can't actually
- # go into an XML document because it means nothing
- # outside of Python.
- declared_encoding = None
- if declared_encoding is not None:
- encoding_part = ' encoding="%s"' % declared_encoding
- prefix = '\n' % encoding_part
- else:
- prefix = ""
-
- # Prior to 4.13.0, the first argument to this method was a
- # bool called pretty_print, which gave the method a different
- # signature from its superclass implementation, Tag.decode.
- #
- # The signatures of the two methods now match, but just in
- # case someone is still passing a boolean in as the first
- # argument to this method (or a keyword argument with the old
- # name), we can handle it and put out a DeprecationWarning.
- warning: Optional[str] = None
- pretty_print: Optional[bool] = None
- if isinstance(indent_level, bool):
- if indent_level is True:
- indent_level = 0
- elif indent_level is False:
- indent_level = None
- warning = f"As of 4.13.0, the first argument to BeautifulSoup.decode has been changed from bool to int, to match Tag.decode. Pass in a value of {indent_level} instead."
- else:
- pretty_print = kwargs.pop("pretty_print", None)
- assert not kwargs
- if pretty_print is not None:
- if pretty_print is True:
- indent_level = 0
- elif pretty_print is False:
- indent_level = None
- warning = f"As of 4.13.0, the pretty_print argument to BeautifulSoup.decode has been removed, to match Tag.decode. Pass in a value of indent_level={indent_level} instead."
-
- if warning:
- warnings.warn(warning, DeprecationWarning, stacklevel=2)
- elif indent_level is False or pretty_print is False:
- indent_level = None
- return prefix + super(BeautifulSoup, self).decode(
- indent_level, eventual_encoding, formatter, iterator
- )
-
-
-# Aliases to make it easier to get started quickly, e.g. 'from bs4 import _soup'
-_s = BeautifulSoup
-_soup = BeautifulSoup
-
-
-class BeautifulStoneSoup(BeautifulSoup):
- """Deprecated interface to an XML parser."""
-
- def __init__(self, *args: Any, **kwargs: Any):
- kwargs["features"] = "xml"
- warnings.warn(
- "The BeautifulStoneSoup class was deprecated in version 4.0.0. Instead of using "
- 'it, pass features="xml" into the BeautifulSoup constructor.',
- DeprecationWarning,
- stacklevel=2,
- )
- super(BeautifulStoneSoup, self).__init__(*args, **kwargs)
-
-
-# If this file is run as a script, act as an HTML pretty-printer.
-if __name__ == "__main__":
- import sys
-
- soup = BeautifulSoup(sys.stdin)
- print((soup.prettify()))
diff --git a/myenv/Lib/site-packages/bs4/__pycache__/__init__.cpython-38.pyc b/myenv/Lib/site-packages/bs4/__pycache__/__init__.cpython-38.pyc
deleted file mode 100644
index f36890a..0000000
Binary files a/myenv/Lib/site-packages/bs4/__pycache__/__init__.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/bs4/__pycache__/_deprecation.cpython-38.pyc b/myenv/Lib/site-packages/bs4/__pycache__/_deprecation.cpython-38.pyc
deleted file mode 100644
index 9467fbe..0000000
Binary files a/myenv/Lib/site-packages/bs4/__pycache__/_deprecation.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/bs4/__pycache__/_typing.cpython-38.pyc b/myenv/Lib/site-packages/bs4/__pycache__/_typing.cpython-38.pyc
deleted file mode 100644
index 6096785..0000000
Binary files a/myenv/Lib/site-packages/bs4/__pycache__/_typing.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/bs4/__pycache__/_warnings.cpython-38.pyc b/myenv/Lib/site-packages/bs4/__pycache__/_warnings.cpython-38.pyc
deleted file mode 100644
index e454caa..0000000
Binary files a/myenv/Lib/site-packages/bs4/__pycache__/_warnings.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/bs4/__pycache__/css.cpython-38.pyc b/myenv/Lib/site-packages/bs4/__pycache__/css.cpython-38.pyc
deleted file mode 100644
index 849cb9c..0000000
Binary files a/myenv/Lib/site-packages/bs4/__pycache__/css.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/bs4/__pycache__/dammit.cpython-38.pyc b/myenv/Lib/site-packages/bs4/__pycache__/dammit.cpython-38.pyc
deleted file mode 100644
index 993c635..0000000
Binary files a/myenv/Lib/site-packages/bs4/__pycache__/dammit.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/bs4/__pycache__/diagnose.cpython-38.pyc b/myenv/Lib/site-packages/bs4/__pycache__/diagnose.cpython-38.pyc
deleted file mode 100644
index 2630a8a..0000000
Binary files a/myenv/Lib/site-packages/bs4/__pycache__/diagnose.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/bs4/__pycache__/element.cpython-38.pyc b/myenv/Lib/site-packages/bs4/__pycache__/element.cpython-38.pyc
deleted file mode 100644
index db217a6..0000000
Binary files a/myenv/Lib/site-packages/bs4/__pycache__/element.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/bs4/__pycache__/exceptions.cpython-38.pyc b/myenv/Lib/site-packages/bs4/__pycache__/exceptions.cpython-38.pyc
deleted file mode 100644
index 2853e62..0000000
Binary files a/myenv/Lib/site-packages/bs4/__pycache__/exceptions.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/bs4/__pycache__/filter.cpython-38.pyc b/myenv/Lib/site-packages/bs4/__pycache__/filter.cpython-38.pyc
deleted file mode 100644
index 7a3b130..0000000
Binary files a/myenv/Lib/site-packages/bs4/__pycache__/filter.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/bs4/__pycache__/formatter.cpython-38.pyc b/myenv/Lib/site-packages/bs4/__pycache__/formatter.cpython-38.pyc
deleted file mode 100644
index 7eb4b8e..0000000
Binary files a/myenv/Lib/site-packages/bs4/__pycache__/formatter.cpython-38.pyc and /dev/null differ
diff --git a/myenv/Lib/site-packages/bs4/_deprecation.py b/myenv/Lib/site-packages/bs4/_deprecation.py
deleted file mode 100644
index a7b5685..0000000
--- a/myenv/Lib/site-packages/bs4/_deprecation.py
+++ /dev/null
@@ -1,80 +0,0 @@
-"""Helper functions for deprecation.
-
-This interface is itself unstable and may change without warning. Do
-not use these functions yourself, even as a joke. The underscores are
-there for a reason. No support will be given.
-
-In particular, most of this will go away without warning once
-Beautiful Soup drops support for Python 3.11, since Python 3.12
-defines a `@typing.deprecated()
-decorator. `_
-"""
-
-import functools
-import warnings
-
-from typing import (
- Any,
- Callable,
-)
-
-
-def _deprecated_alias(old_name: str, new_name: str, version: str):
- """Alias one attribute name to another for backward compatibility
-
- :meta private:
- """
-
- @property # type:ignore
- def alias(self) -> Any:
- ":meta private:"
- warnings.warn(
- f"Access to deprecated property {old_name}. (Replaced by {new_name}) -- Deprecated since version {version}.",
- DeprecationWarning,
- stacklevel=2,
- )
- return getattr(self, new_name)
-
- @alias.setter
- def alias(self, value: str) -> None:
- ":meta private:"
- warnings.warn(
- f"Write to deprecated property {old_name}. (Replaced by {new_name}) -- Deprecated since version {version}.",
- DeprecationWarning,
- stacklevel=2,
- )
- return setattr(self, new_name, value)
-
- return alias
-
-
-def _deprecated_function_alias(
- old_name: str, new_name: str, version: str
-) -> Callable[[Any], Any]:
- def alias(self, *args: Any, **kwargs: Any) -> Any:
- ":meta private:"
- warnings.warn(
- f"Call to deprecated method {old_name}. (Replaced by {new_name}) -- Deprecated since version {version}.",
- DeprecationWarning,
- stacklevel=2,
- )
- return getattr(self, new_name)(*args, **kwargs)
-
- return alias
-
-
-def _deprecated(replaced_by: str, version: str) -> Callable:
- def deprecate(func: Callable) -> Callable:
- @functools.wraps(func)
- def with_warning(*args: Any, **kwargs: Any) -> Any:
- ":meta private:"
- warnings.warn(
- f"Call to deprecated method {func.__name__}. (Replaced by {replaced_by}) -- Deprecated since version {version}.",
- DeprecationWarning,
- stacklevel=2,
- )
- return func(*args, **kwargs)
-
- return with_warning
-
- return deprecate
diff --git a/myenv/Lib/site-packages/bs4/_typing.py b/myenv/Lib/site-packages/bs4/_typing.py
deleted file mode 100644
index 0ab69df..0000000
--- a/myenv/Lib/site-packages/bs4/_typing.py
+++ /dev/null
@@ -1,205 +0,0 @@
-# Custom type aliases used throughout Beautiful Soup to improve readability.
-
-# Notes on improvements to the type system in newer versions of Python
-# that can be used once Beautiful Soup drops support for older
-# versions:
-#
-# * ClassVar can be put on class variables now.
-# * In 3.10, x|y is an accepted shorthand for Union[x,y].
-# * In 3.10, TypeAlias gains capabilities that can be used to
-# improve the tree matching types (I don't remember what, exactly).
-# * In 3.9 it's possible to specialize the re.Match type,
-# e.g. re.Match[str]. In 3.8 there's a typing.re namespace for this,
-# but it's removed in 3.12, so to support the widest possible set of
-# versions I'm not using it.
-
-from typing_extensions import (
- runtime_checkable,
- Protocol,
- TypeAlias,
-)
-from typing import (
- Any,
- Callable,
- Dict,
- IO,
- Iterable,
- Mapping,
- Optional,
- Pattern,
- TYPE_CHECKING,
- Union,
-)
-
-if TYPE_CHECKING:
- from bs4.element import (
- AttributeValueList,
- NamespacedAttribute,
- NavigableString,
- PageElement,
- ResultSet,
- Tag,
- )
-
-
-@runtime_checkable
-class _RegularExpressionProtocol(Protocol):
- """A protocol object which can accept either Python's built-in
- `re.Pattern` objects, or the similar ``Regex`` objects defined by the
- third-party ``regex`` package.
- """
-
- def search(
- self, string: str, pos: int = ..., endpos: int = ...
- ) -> Optional[Any]: ...
-
- @property
- def pattern(self) -> str: ...
-
-
-# Aliases for markup in various stages of processing.
-#
-#: The rawest form of markup: either a string, bytestring, or an open filehandle.
-_IncomingMarkup: TypeAlias = Union[str, bytes, IO[str], IO[bytes]]
-
-#: Markup that is in memory but has (potentially) yet to be converted
-#: to Unicode.
-_RawMarkup: TypeAlias = Union[str, bytes]
-
-# Aliases for character encodings
-#
-
-#: A data encoding.
-_Encoding: TypeAlias = str
-
-#: One or more data encodings.
-_Encodings: TypeAlias = Iterable[_Encoding]
-
-# Aliases for XML namespaces
-#
-
-#: The prefix for an XML namespace.
-_NamespacePrefix: TypeAlias = str
-
-#: The URL of an XML namespace
-_NamespaceURL: TypeAlias = str
-
-#: A mapping of prefixes to namespace URLs.
-_NamespaceMapping: TypeAlias = Dict[_NamespacePrefix, _NamespaceURL]
-
-#: A mapping of namespace URLs to prefixes
-_InvertedNamespaceMapping: TypeAlias = Dict[_NamespaceURL, _NamespacePrefix]
-
-# Aliases for the attribute values associated with HTML/XML tags.
-#
-
-#: The value associated with an HTML or XML attribute. This is the
-#: relatively unprocessed value Beautiful Soup expects to come from a
-#: `TreeBuilder`.
-_RawAttributeValue: TypeAlias = str
-
-#: A dictionary of names to `_RawAttributeValue` objects. This is how
-#: Beautiful Soup expects a `TreeBuilder` to represent a tag's
-#: attribute values.
-_RawAttributeValues: TypeAlias = (
- "Mapping[Union[str, NamespacedAttribute], _RawAttributeValue]"
-)
-
-#: An attribute value in its final form, as stored in the
-# `Tag` class, after it has been processed and (in some cases)
-# split into a list of strings.
-_AttributeValue: TypeAlias = Union[str, "AttributeValueList"]
-
-#: A dictionary of names to :py:data:`_AttributeValue` objects. This is what
-#: a tag's attributes look like after processing.
-_AttributeValues: TypeAlias = Dict[str, _AttributeValue]
-
-#: The methods that deal with turning :py:data:`_RawAttributeValue` into
-#: :py:data:`_AttributeValue` may be called several times, even after the values
-#: are already processed (e.g. when cloning a tag), so they need to
-#: be able to acommodate both possibilities.
-_RawOrProcessedAttributeValues: TypeAlias = Union[_RawAttributeValues, _AttributeValues]
-
-#: A number of tree manipulation methods can take either a `PageElement` or a
-#: normal Python string (which will be converted to a `NavigableString`).
-_InsertableElement: TypeAlias = Union["PageElement", str]
-
-# Aliases to represent the many possibilities for matching bits of a
-# parse tree.
-#
-# This is very complicated because we're applying a formal type system
-# to some very DWIM code. The types we end up with will be the types
-# of the arguments to the SoupStrainer constructor and (more
-# familiarly to Beautiful Soup users) the find* methods.
-
-#: A function that takes a PageElement and returns a yes-or-no answer.
-_PageElementMatchFunction: TypeAlias = Callable[["PageElement"], bool]
-
-#: A function that takes the raw parsed ingredients of a markup tag
-#: and returns a yes-or-no answer.
-# Not necessary at the moment.
-# _AllowTagCreationFunction:TypeAlias = Callable[[Optional[str], str, Optional[_RawAttributeValues]], bool]
-
-#: A function that takes the raw parsed ingredients of a markup string node
-#: and returns a yes-or-no answer.
-# Not necessary at the moment.
-# _AllowStringCreationFunction:TypeAlias = Callable[[Optional[str]], bool]
-
-#: A function that takes a `Tag` and returns a yes-or-no answer.
-#: A `TagNameMatchRule` expects this kind of function, if you're
-#: going to pass it a function.
-_TagMatchFunction: TypeAlias = Callable[["Tag"], bool]
-
-#: A function that takes a string (or None) and returns a yes-or-no
-#: answer. An `AttributeValueMatchRule` expects this kind of function, if
-#: you're going to pass it a function.
-_NullableStringMatchFunction: TypeAlias = Callable[[Optional[str]], bool]
-
-#: A function that takes a string and returns a yes-or-no answer. A
-# `StringMatchRule` expects this kind of function, if you're going to
-# pass it a function.
-_StringMatchFunction: TypeAlias = Callable[[str], bool]
-
-#: Either a tag name, an attribute value or a string can be matched
-#: against a string, bytestring, regular expression, or a boolean.
-_BaseStrainable: TypeAlias = Union[str, bytes, Pattern[str], bool]
-
-#: A tag can be matched either with the `_BaseStrainable` options, or
-#: using a function that takes the `Tag` as its sole argument.
-_BaseStrainableElement: TypeAlias = Union[_BaseStrainable, _TagMatchFunction]
-
-#: A tag's attribute value can be matched either with the
-#: `_BaseStrainable` options, or using a function that takes that
-#: value as its sole argument.
-_BaseStrainableAttribute: TypeAlias = Union[_BaseStrainable, _NullableStringMatchFunction]
-
-#: A tag can be matched using either a single criterion or a list of
-#: criteria.
-_StrainableElement: TypeAlias = Union[
- _BaseStrainableElement, Iterable[_BaseStrainableElement]
-]
-
-#: An attribute value can be matched using either a single criterion
-#: or a list of criteria.
-_StrainableAttribute: TypeAlias = Union[
- _BaseStrainableAttribute, Iterable[_BaseStrainableAttribute]
-]
-
-#: An string can be matched using the same techniques as
-#: an attribute value.
-_StrainableString: TypeAlias = _StrainableAttribute
-
-#: A dictionary may be used to match against multiple attribute vlaues at once.
-_StrainableAttributes: TypeAlias = Dict[str, _StrainableAttribute]
-
-#: Many Beautiful soup methods return a PageElement or an ResultSet of
-#: PageElements. A PageElement is either a Tag or a NavigableString.
-#: These convenience aliases make it easier for IDE users to see which methods
-#: are available on the objects they're dealing with.
-_OneElement: TypeAlias = Union["PageElement", "Tag", "NavigableString"]
-_AtMostOneElement: TypeAlias = Optional[_OneElement]
-_AtMostOneTag: TypeAlias = Optional["Tag"]
-_AtMostOneNavigableString: TypeAlias = Optional["NavigableString"]
-_QueryResults: TypeAlias = "ResultSet[_OneElement]"
-_SomeTags: TypeAlias = "ResultSet[Tag]"
-_SomeNavigableStrings: TypeAlias = "ResultSet[NavigableString]"
diff --git a/myenv/Lib/site-packages/bs4/_warnings.py b/myenv/Lib/site-packages/bs4/_warnings.py
deleted file mode 100644
index 4309473..0000000
--- a/myenv/Lib/site-packages/bs4/_warnings.py
+++ /dev/null
@@ -1,98 +0,0 @@
-"""Define some custom warnings."""
-
-
-class GuessedAtParserWarning(UserWarning):
- """The warning issued when BeautifulSoup has to guess what parser to
- use -- probably because no parser was specified in the constructor.
- """
-
- MESSAGE: str = """No parser was explicitly specified, so I'm using the best available %(markup_type)s parser for this system ("%(parser)s"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.
-
-The code that caused this warning is on line %(line_number)s of the file %(filename)s. To get rid of this warning, pass the additional argument 'features="%(parser)s"' to the BeautifulSoup constructor.
-"""
-
-
-class UnusualUsageWarning(UserWarning):
- """A superclass for warnings issued when Beautiful Soup sees
- something that is typically the result of a mistake in the calling
- code, but might be intentional on the part of the user. If it is
- in fact intentional, you can filter the individual warning class
- to get rid of the warning. If you don't like Beautiful Soup
- second-guessing what you are doing, you can filter the
- UnusualUsageWarningclass itself and get rid of these entirely.
- """
-
-
-class MarkupResemblesLocatorWarning(UnusualUsageWarning):
- """The warning issued when BeautifulSoup is given 'markup' that
- actually looks like a resource locator -- a URL or a path to a file
- on disk.
- """
-
- #: :meta private:
- GENERIC_MESSAGE: str = """
-
-However, if you want to parse some data that happens to look like a %(what)s, then nothing has gone wrong: you are using Beautiful Soup correctly, and this warning is spurious and can be filtered. To make this warning go away, run this code before calling the BeautifulSoup constructor:
-
- from bs4 import MarkupResemblesLocatorWarning
- import warnings
-
- warnings.filterwarnings("ignore", category=MarkupResemblesLocatorWarning)
- """
-
- URL_MESSAGE: str = (
- """The input passed in on this line looks more like a URL than HTML or XML.
-
-If you meant to use Beautiful Soup to parse the web page found at a certain URL, then something has gone wrong. You should use an Python package like 'requests' to fetch the content behind the URL. Once you have the content as a string, you can feed that string into Beautiful Soup."""
- + GENERIC_MESSAGE
- )
-
- FILENAME_MESSAGE: str = (
- """The input passed in on this line looks more like a filename than HTML or XML.
-
-If you meant to use Beautiful Soup to parse the contents of a file on disk, then something has gone wrong. You should open the file first, using code like this:
-
- filehandle = open(your filename)
-
-You can then feed the open filehandle into Beautiful Soup instead of using the filename."""
- + GENERIC_MESSAGE
- )
-
-
-class AttributeResemblesVariableWarning(UnusualUsageWarning, SyntaxWarning):
- """The warning issued when Beautiful Soup suspects a provided
- attribute name may actually be the misspelled name of a Beautiful
- Soup variable. Generally speaking, this is only used in cases like
- "_class" where it's very unlikely the user would be referencing an
- XML attribute with that name.
- """
-
- MESSAGE: str = """%(original)r is an unusual attribute name and is a common misspelling for %(autocorrect)r.
-
-If you meant %(autocorrect)r, change your code to use it, and this warning will go away.
-
-If you really did mean to check the %(original)r attribute, this warning is spurious and can be filtered. To make it go away, run this code before creating your BeautifulSoup object:
-
- from bs4 import AttributeResemblesVariableWarning
- import warnings
-
- warnings.filterwarnings("ignore", category=AttributeResemblesVariableWarning)
-"""
-
-
-class XMLParsedAsHTMLWarning(UnusualUsageWarning):
- """The warning issued when an HTML parser is used to parse
- XML that is not (as far as we can tell) XHTML.
- """
-
- MESSAGE: str = """It looks like you're using an HTML parser to parse an XML document.
-
-Assuming this really is an XML document, what you're doing might work, but you should know that using an XML parser will be more reliable. To parse this document as XML, make sure you have the Python package 'lxml' installed, and pass the keyword argument `features="xml"` into the BeautifulSoup constructor.
-
-If you want or need to use an HTML parser on this document, you can make this warning go away by filtering it. To do that, run this code before calling the BeautifulSoup constructor:
-
- from bs4 import XMLParsedAsHTMLWarning
- import warnings
-
- warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning)
-"""
diff --git a/myenv/Lib/site-packages/bs4/builder/__init__.py b/myenv/Lib/site-packages/bs4/builder/__init__.py
deleted file mode 100644
index 4aae4d3..0000000
--- a/myenv/Lib/site-packages/bs4/builder/__init__.py
+++ /dev/null
@@ -1,848 +0,0 @@
-from __future__ import annotations
-
-# Use of this source code is governed by the MIT license.
-__license__ = "MIT"
-
-from collections import defaultdict
-import re
-from types import ModuleType
-from typing import (
- Any,
- cast,
- Dict,
- Iterable,
- List,
- Optional,
- Pattern,
- Set,
- Tuple,
- Type,
- TYPE_CHECKING,
-)
-import warnings
-import sys
-from bs4.element import (
- AttributeDict,
- AttributeValueList,
- CharsetMetaAttributeValue,
- ContentMetaAttributeValue,
- RubyParenthesisString,
- RubyTextString,
- Stylesheet,
- Script,
- TemplateString,
- nonwhitespace_re,
-)
-
-# Exceptions were moved to their own module in 4.13. Import here for
-# backwards compatibility.
-from bs4.exceptions import ParserRejectedMarkup
-
-from bs4._typing import (
- _AttributeValues,
- _RawAttributeValue,
-)
-
-from bs4._warnings import XMLParsedAsHTMLWarning
-
-if TYPE_CHECKING:
- from bs4 import BeautifulSoup
- from bs4.element import (
- NavigableString,
- Tag,
- )
- from bs4._typing import (
- _AttributeValue,
- _Encoding,
- _Encodings,
- _RawOrProcessedAttributeValues,
- _RawMarkup,
- )
-
-__all__ = [
- "HTMLTreeBuilder",
- "SAXTreeBuilder",
- "TreeBuilder",
- "TreeBuilderRegistry",
-]
-
-# Some useful features for a TreeBuilder to have.
-FAST = "fast"
-PERMISSIVE = "permissive"
-STRICT = "strict"
-XML = "xml"
-HTML = "html"
-HTML_5 = "html5"
-
-__all__ = [
- "TreeBuilderRegistry",
- "TreeBuilder",
- "HTMLTreeBuilder",
- "DetectsXMLParsedAsHTML",
-
- "ParserRejectedMarkup", # backwards compatibility only as of 4.13.0
-]
-
-class TreeBuilderRegistry(object):
- """A way of looking up TreeBuilder subclasses by their name or by desired
- features.
- """
-
- builders_for_feature: Dict[str, List[Type[TreeBuilder]]]
- builders: List[Type[TreeBuilder]]
-
- def __init__(self) -> None:
- self.builders_for_feature = defaultdict(list)
- self.builders = []
-
- def register(self, treebuilder_class: type[TreeBuilder]) -> None:
- """Register a treebuilder based on its advertised features.
-
- :param treebuilder_class: A subclass of `TreeBuilder`. its
- `TreeBuilder.features` attribute should list its features.
- """
- for feature in treebuilder_class.features:
- self.builders_for_feature[feature].insert(0, treebuilder_class)
- self.builders.insert(0, treebuilder_class)
-
- def lookup(self, *features: str) -> Optional[Type[TreeBuilder]]:
- """Look up a TreeBuilder subclass with the desired features.
-
- :param features: A list of features to look for. If none are
- provided, the most recently registered TreeBuilder subclass
- will be used.
- :return: A TreeBuilder subclass, or None if there's no
- registered subclass with all the requested features.
- """
- if len(self.builders) == 0:
- # There are no builders at all.
- return None
-
- if len(features) == 0:
- # They didn't ask for any features. Give them the most
- # recently registered builder.
- return self.builders[0]
-
- # Go down the list of features in order, and eliminate any builders
- # that don't match every feature.
- feature_list = list(features)
- feature_list.reverse()
- candidates = None
- candidate_set = None
- while len(feature_list) > 0:
- feature = feature_list.pop()
- we_have_the_feature = self.builders_for_feature.get(feature, [])
- if len(we_have_the_feature) > 0:
- if candidates is None:
- candidates = we_have_the_feature
- candidate_set = set(candidates)
- elif candidate_set is not None:
- # Eliminate any candidates that don't have this feature.
- candidate_set = candidate_set.intersection(set(we_have_the_feature))
-
- # The only valid candidates are the ones in candidate_set.
- # Go through the original list of candidates and pick the first one
- # that's in candidate_set.
- if candidate_set is None or candidates is None:
- return None
- for candidate in candidates:
- if candidate in candidate_set:
- return candidate
- return None
-
-
-#: The `BeautifulSoup` constructor will take a list of features
-#: and use it to look up `TreeBuilder` classes in this registry.
-builder_registry: TreeBuilderRegistry = TreeBuilderRegistry()
-
-
-class TreeBuilder(object):
- """Turn a textual document into a Beautiful Soup object tree.
-
- This is an abstract superclass which smooths out the behavior of
- different parser libraries into a single, unified interface.
-
- :param multi_valued_attributes: If this is set to None, the
- TreeBuilder will not turn any values for attributes like
- 'class' into lists. Setting this to a dictionary will
- customize this behavior; look at :py:attr:`bs4.builder.HTMLTreeBuilder.DEFAULT_CDATA_LIST_ATTRIBUTES`
- for an example.
-
- Internally, these are called "CDATA list attributes", but that
- probably doesn't make sense to an end-user, so the argument name
- is ``multi_valued_attributes``.
-
- :param preserve_whitespace_tags: A set of tags to treat
- the way
tags are treated in HTML. Tags in this set
- are immune from pretty-printing; their contents will always be
- output as-is.
-
- :param string_containers: A dictionary mapping tag names to
- the classes that should be instantiated to contain the textual
- contents of those tags. The default is to use NavigableString
- for every tag, no matter what the name. You can override the
- default by changing :py:attr:`DEFAULT_STRING_CONTAINERS`.
-
- :param store_line_numbers: If the parser keeps track of the line
- numbers and positions of the original markup, that information
- will, by default, be stored in each corresponding
- :py:class:`bs4.element.Tag` object. You can turn this off by
- passing store_line_numbers=False; then Tag.sourcepos and
- Tag.sourceline will always be None. If the parser you're using
- doesn't keep track of this information, then store_line_numbers
- is irrelevant.
-
- :param attribute_dict_class: The value of a multi-valued attribute
- (such as HTML's 'class') willl be stored in an instance of this
- class. The default is Beautiful Soup's built-in
- `AttributeValueList`, which is a normal Python list, and you
- will probably never need to change it.
- """
-
- USE_DEFAULT: Any = object() #: :meta private:
-
- def __init__(
- self,
- multi_valued_attributes: Dict[str, Set[str]] = USE_DEFAULT,
- preserve_whitespace_tags: Set[str] = USE_DEFAULT,
- store_line_numbers: bool = USE_DEFAULT,
- string_containers: Dict[str, Type[NavigableString]] = USE_DEFAULT,
- empty_element_tags: Set[str] = USE_DEFAULT,
- attribute_dict_class: Type[AttributeDict] = AttributeDict,
- attribute_value_list_class: Type[AttributeValueList] = AttributeValueList,
- ):
- self.soup = None
- if multi_valued_attributes is self.USE_DEFAULT:
- multi_valued_attributes = self.DEFAULT_CDATA_LIST_ATTRIBUTES
- self.cdata_list_attributes = multi_valued_attributes
- if preserve_whitespace_tags is self.USE_DEFAULT:
- preserve_whitespace_tags = self.DEFAULT_PRESERVE_WHITESPACE_TAGS
- self.preserve_whitespace_tags = preserve_whitespace_tags
- if empty_element_tags is self.USE_DEFAULT:
- self.empty_element_tags = self.DEFAULT_EMPTY_ELEMENT_TAGS
- else:
- self.empty_element_tags = empty_element_tags
- # TODO: store_line_numbers is probably irrelevant now that
- # the behavior of sourceline and sourcepos has been made consistent
- # everywhere.
- if store_line_numbers == self.USE_DEFAULT:
- store_line_numbers = self.TRACKS_LINE_NUMBERS
- self.store_line_numbers = store_line_numbers
- if string_containers == self.USE_DEFAULT:
- string_containers = self.DEFAULT_STRING_CONTAINERS
- self.string_containers = string_containers
- self.attribute_dict_class = attribute_dict_class
- self.attribute_value_list_class = attribute_value_list_class
-
- NAME: str = "[Unknown tree builder]"
- ALTERNATE_NAMES: Iterable[str] = []
- features: Iterable[str] = []
-
- is_xml: bool = False
- picklable: bool = False
-
- soup: Optional[BeautifulSoup] #: :meta private:
-
- #: A tag will be considered an empty-element
- #: tag when and only when it has no contents.
- empty_element_tags: Optional[Set[str]] = None #: :meta private:
- cdata_list_attributes: Dict[str, Set[str]] #: :meta private:
- preserve_whitespace_tags: Set[str] #: :meta private:
- string_containers: Dict[str, Type[NavigableString]] #: :meta private:
- tracks_line_numbers: bool #: :meta private:
-
- #: A value for these tag/attribute combinations is a space- or
- #: comma-separated list of CDATA, rather than a single CDATA.
- DEFAULT_CDATA_LIST_ATTRIBUTES: Dict[str, Set[str]] = defaultdict(set)
-
- #: Whitespace should be preserved inside these tags.
- DEFAULT_PRESERVE_WHITESPACE_TAGS: Set[str] = set()
-
- #: The textual contents of tags with these names should be
- #: instantiated with some class other than `bs4.element.NavigableString`.
- DEFAULT_STRING_CONTAINERS: Dict[str, Type[bs4.element.NavigableString]] = {} # type:ignore
-
- #: By default, tags are treated as empty-element tags if they have
- #: no contents--that is, using XML rules. HTMLTreeBuilder
- #: defines a different set of DEFAULT_EMPTY_ELEMENT_TAGS based on the
- #: HTML 4 and HTML5 standards.
- DEFAULT_EMPTY_ELEMENT_TAGS: Optional[Set[str]] = None
-
- #: Most parsers don't keep track of line numbers.
- TRACKS_LINE_NUMBERS: bool = False
-
- def initialize_soup(self, soup: BeautifulSoup) -> None:
- """The BeautifulSoup object has been initialized and is now
- being associated with the TreeBuilder.
-
- :param soup: A BeautifulSoup object.
- """
- self.soup = soup
-
- def reset(self) -> None:
- """Do any work necessary to reset the underlying parser
- for a new document.
-
- By default, this does nothing.
- """
- pass
-
- def can_be_empty_element(self, tag_name: str) -> bool:
- """Might a tag with this name be an empty-element tag?
-
- The final markup may or may not actually present this tag as
- self-closing.
-
- For instance: an HTMLBuilder does not consider a
tag to be
- an empty-element tag (it's not in
- HTMLBuilder.empty_element_tags). This means an empty
tag
- will be presented as "
", not "" or "
".
-
- The default implementation has no opinion about which tags are
- empty-element tags, so a tag will be presented as an
- empty-element tag if and only if it has no children.
- "" will become "", and "bar" will
- be left alone.
-
- :param tag_name: The name of a markup tag.
- """
- if self.empty_element_tags is None:
- return True
- return tag_name in self.empty_element_tags
-
- def feed(self, markup: _RawMarkup) -> None:
- """Run incoming markup through some parsing process."""
- raise NotImplementedError()
-
- def prepare_markup(
- self,
- markup: _RawMarkup,
- user_specified_encoding: Optional[_Encoding] = None,
- document_declared_encoding: Optional[_Encoding] = None,
- exclude_encodings: Optional[_Encodings] = None,
- ) -> Iterable[Tuple[_RawMarkup, Optional[_Encoding], Optional[_Encoding], bool]]:
- """Run any preliminary steps necessary to make incoming markup
- acceptable to the parser.
-
- :param markup: The markup that's about to be parsed.
- :param user_specified_encoding: The user asked to try this encoding
- to convert the markup into a Unicode string.
- :param document_declared_encoding: The markup itself claims to be
- in this encoding. NOTE: This argument is not used by the
- calling code and can probably be removed.
- :param exclude_encodings: The user asked *not* to try any of
- these encodings.
-
- :yield: A series of 4-tuples: (markup, encoding, declared encoding,
- has undergone character replacement)
-
- Each 4-tuple represents a strategy that the parser can try
- to convert the document to Unicode and parse it. Each
- strategy will be tried in turn.
-
- By default, the only strategy is to parse the markup
- as-is. See `LXMLTreeBuilderForXML` and
- `HTMLParserTreeBuilder` for implementations that take into
- account the quirks of particular parsers.
-
- :meta private:
-
- """
- yield markup, None, None, False
-
- def test_fragment_to_document(self, fragment: str) -> str:
- """Wrap an HTML fragment to make it look like a document.
-
- Different parsers do this differently. For instance, lxml
- introduces an empty
tag, and html5lib
- doesn't. Abstracting this away lets us write simple tests
- which run HTML fragments through the parser and compare the
- results against other HTML fragments.
-
- This method should not be used outside of unit tests.
-
- :param fragment: A fragment of HTML.
- :return: A full HTML document.
- :meta private:
- """
- return fragment
-
- def set_up_substitutions(self, tag: Tag) -> bool:
- """Set up any substitutions that will need to be performed on
- a `Tag` when it's output as a string.
-
- By default, this does nothing. See `HTMLTreeBuilder` for a
- case where this is used.
-
- :return: Whether or not a substitution was performed.
- :meta private:
- """
- return False
-
- def _replace_cdata_list_attribute_values(
- self, tag_name: str, attrs: _RawOrProcessedAttributeValues
- ) -> _AttributeValues:
- """When an attribute value is associated with a tag that can
- have multiple values for that attribute, convert the string
- value to a list of strings.
-
- Basically, replaces class="foo bar" with class=["foo", "bar"]
-
- NOTE: This method modifies its input in place.
-
- :param tag_name: The name of a tag.
- :param attrs: A dictionary containing the tag's attributes.
- Any appropriate attribute values will be modified in place.
- :return: The modified dictionary that was originally passed in.
- """
-
- # First, cast the attrs dict to _AttributeValues. This might
- # not be accurate yet, but it will be by the time this method
- # returns.
- modified_attrs = cast(_AttributeValues, attrs)
- if not modified_attrs or not self.cdata_list_attributes:
- # Nothing to do.
- return modified_attrs
-
- # There is at least a possibility that we need to modify one of
- # the attribute values.
- universal: Set[str] = self.cdata_list_attributes.get("*", set())
- tag_specific = self.cdata_list_attributes.get(tag_name.lower(), None)
- for attr in list(modified_attrs.keys()):
- modified_value: _AttributeValue
- if attr in universal or (tag_specific and attr in tag_specific):
- # We have a "class"-type attribute whose string
- # value is a whitespace-separated list of
- # values. Split it into a list.
- original_value: _AttributeValue = modified_attrs[attr]
- if isinstance(original_value, _RawAttributeValue):
- # This is a _RawAttributeValue (a string) that
- # needs to be split and converted to a
- # AttributeValueList so it can be an
- # _AttributeValue.
- modified_value = self.attribute_value_list_class(
- nonwhitespace_re.findall(original_value)
- )
- else:
- # html5lib calls setAttributes twice for the
- # same tag when rearranging the parse tree. On
- # the second call the attribute value here is
- # already a list. This can also happen when a
- # Tag object is cloned. If this happens, leave
- # the value alone rather than trying to split
- # it again.
- modified_value = original_value
- modified_attrs[attr] = modified_value
- return modified_attrs
-
-
-class SAXTreeBuilder(TreeBuilder):
- """A Beautiful Soup treebuilder that listens for SAX events.
-
- This is not currently used for anything, and it will be removed
- soon. It was a good idea, but it wasn't properly integrated into the
- rest of Beautiful Soup, so there have been long stretches where it
- hasn't worked properly.
- """
-
- def __init__(self, *args: Any, **kwargs: Any) -> None:
- warnings.warn(
- "The SAXTreeBuilder class was deprecated in 4.13.0 and will be removed soon thereafter. It is completely untested and probably doesn't work; do not use it.",
- DeprecationWarning,
- stacklevel=2,
- )
- super(SAXTreeBuilder, self).__init__(*args, **kwargs)
-
- def feed(self, markup: _RawMarkup) -> None:
- raise NotImplementedError()
-
- def close(self) -> None:
- pass
-
- def startElement(self, name: str, attrs: Dict[str, str]) -> None:
- attrs = AttributeDict((key[1], value) for key, value in list(attrs.items()))
- # print("Start %s, %r" % (name, attrs))
- assert self.soup is not None
- self.soup.handle_starttag(name, None, None, attrs)
-
- def endElement(self, name: str) -> None:
- # print("End %s" % name)
- assert self.soup is not None
- self.soup.handle_endtag(name)
-
- def startElementNS(
- self, nsTuple: Tuple[str, str], nodeName: str, attrs: Dict[str, str]
- ) -> None:
- # Throw away (ns, nodeName) for now.
- self.startElement(nodeName, attrs)
-
- def endElementNS(self, nsTuple: Tuple[str, str], nodeName: str) -> None:
- # Throw away (ns, nodeName) for now.
- self.endElement(nodeName)
- # handler.endElementNS((ns, node.nodeName), node.nodeName)
-
- def startPrefixMapping(self, prefix: str, nodeValue: str) -> None:
- # Ignore the prefix for now.
- pass
-
- def endPrefixMapping(self, prefix: str) -> None:
- # Ignore the prefix for now.
- # handler.endPrefixMapping(prefix)
- pass
-
- def characters(self, content: str) -> None:
- assert self.soup is not None
- self.soup.handle_data(content)
-
- def startDocument(self) -> None:
- pass
-
- def endDocument(self) -> None:
- pass
-
-
-class HTMLTreeBuilder(TreeBuilder):
- """This TreeBuilder knows facts about HTML, such as which tags are treated
- specially by the HTML standard.
- """
-
- #: Some HTML tags are defined as having no contents. Beautiful Soup
- #: treats these specially.
- DEFAULT_EMPTY_ELEMENT_TAGS: Optional[Set[str]] = set(
- [
- # These are from HTML5.
- "area",
- "base",
- "br",
- "col",
- "embed",
- "hr",
- "img",
- "input",
- "keygen",
- "link",
- "menuitem",
- "meta",
- "param",
- "source",
- "track",
- "wbr",
- # These are from earlier versions of HTML and are removed in HTML5.
- "basefont",
- "bgsound",
- "command",
- "frame",
- "image",
- "isindex",
- "nextid",
- "spacer",
- ]
- )
-
- #: The HTML standard defines these tags as block-level elements. Beautiful
- #: Soup does not treat these elements differently from other elements,
- #: but it may do so eventually, and this information is available if
- #: you need to use it.
- DEFAULT_BLOCK_ELEMENTS: Set[str] = set(
- [
- "address",
- "article",
- "aside",
- "blockquote",
- "canvas",
- "dd",
- "div",
- "dl",
- "dt",
- "fieldset",
- "figcaption",
- "figure",
- "footer",
- "form",
- "h1",
- "h2",
- "h3",
- "h4",
- "h5",
- "h6",
- "header",
- "hr",
- "li",
- "main",
- "nav",
- "noscript",
- "ol",
- "output",
- "p",
- "pre",
- "section",
- "table",
- "tfoot",
- "ul",
- "video",
- ]
- )
-
- #: These HTML tags need special treatment so they can be
- #: represented by a string class other than `bs4.element.NavigableString`.
- #:
- #: For some of these tags, it's because the HTML standard defines
- #: an unusual content model for them. I made this list by going
- #: through the HTML spec
- #: (https://html.spec.whatwg.org/#metadata-content) and looking for
- #: "metadata content" elements that can contain strings.
- #:
- #: The Ruby tags (