diff --git a/alembic.ini b/alembic.ini
new file mode 100644
index 000000000..b67d73c20
--- /dev/null
+++ b/alembic.ini
@@ -0,0 +1,105 @@
+# A generic, single database configuration.
+
+[alembic]
+# path to migration scripts
+script_location = migrations
+
+# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
+# Uncomment the line below if you want the files to be prepended with date and time
+# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
+# for all available tokens
+# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
+
+# sys.path path, will be prepended to sys.path if present.
+# defaults to the current working directory.
+prepend_sys_path = .
+
+# timezone to use when rendering the date within the migration file
+# as well as the filename.
+# If specified, requires the python-dateutil library that can be
+# installed by adding `alembic[tz]` to the pip requirements
+# string value is passed to dateutil.tz.gettz()
+# leave blank for localtime
+# timezone =
+
+# max length of characters to apply to the
+# "slug" field
+# truncate_slug_length = 40
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+# set to 'true' to allow .pyc and .pyo files without
+# a source .py file to be detected as revisions in the
+# versions/ directory
+# sourceless = false
+
+# version location specification; This defaults
+# to migrations/versions. When using multiple version
+# directories, initial revisions must be specified with --version-path.
+# The path separator used here should be the separator specified by "version_path_separator" below.
+# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions
+
+# version path separator; As mentioned above, this is the character used to split
+# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
+# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
+# Valid values for version_path_separator are:
+#
+# version_path_separator = :
+# version_path_separator = ;
+# version_path_separator = space
+version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
+
+# the output encoding used when revision files
+# are written from script.py.mako
+# output_encoding = utf-8
+
+sqlalchemy.url = sqlite3:///freebies.db
+
+
+[post_write_hooks]
+# post_write_hooks defines scripts or Python functions that are run
+# on newly generated revision scripts. See the documentation for further
+# detail and examples
+
+# format using "black" - use the console_scripts runner, against the "black" entrypoint
+# hooks = black
+# black.type = console_scripts
+# black.entrypoint = black
+# black.options = -l 79 REVISION_SCRIPT_FILENAME
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/lib/__pycache__/models.cpython-310.pyc b/lib/__pycache__/models.cpython-310.pyc
new file mode 100644
index 000000000..3b6f53a22
Binary files /dev/null and b/lib/__pycache__/models.cpython-310.pyc differ
diff --git a/lib/alembic.ini b/lib/alembic.ini
index 953863ddd..b67d73c20 100644
--- a/lib/alembic.ini
+++ b/lib/alembic.ini
@@ -55,7 +55,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne
# are written from script.py.mako
# output_encoding = utf-8
-sqlalchemy.url = sqlite:///freebies.db
+sqlalchemy.url = sqlite3:///freebies.db
[post_write_hooks]
diff --git a/lib/freebie.db b/lib/freebie.db
new file mode 100644
index 000000000..e69de29bb
diff --git a/lib/freebies.db b/lib/freebies.db
index 12beb1c96..12144916c 100644
Binary files a/lib/freebies.db and b/lib/freebies.db differ
diff --git a/lib/migrations/__pycache__/env.cpython-310.pyc b/lib/migrations/__pycache__/env.cpython-310.pyc
new file mode 100644
index 000000000..d1560eb73
Binary files /dev/null and b/lib/migrations/__pycache__/env.cpython-310.pyc differ
diff --git a/lib/migrations/versions/3b741df497a3_add_company_dev_table.py b/lib/migrations/versions/3b741df497a3_add_company_dev_table.py
new file mode 100644
index 000000000..a8053be5b
--- /dev/null
+++ b/lib/migrations/versions/3b741df497a3_add_company_dev_table.py
@@ -0,0 +1,34 @@
+"""add company_dev table
+
+Revision ID: 3b741df497a3
+Revises: a3e1afdbcc19
+Create Date: 2023-12-07 11:34:09.839206
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '3b741df497a3'
+down_revision = 'a3e1afdbcc19'
+branch_labels = None
+depends_on = None
+
+
+def upgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('companies_devs',
+ sa.Column('company_id', sa.Integer(), nullable=False),
+ sa.Column('dev_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['company_id'], ['companies.id'], name=op.f('fk_companies_devs_company_id_companies')),
+ sa.ForeignKeyConstraint(['dev_id'], ['devs.id'], name=op.f('fk_companies_devs_dev_id_devs')),
+ sa.PrimaryKeyConstraint('company_id', 'dev_id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('companies_devs')
+ # ### end Alembic commands ###
diff --git a/lib/migrations/versions/__pycache__/3b741df497a3_add_company_dev_table.cpython-310.pyc b/lib/migrations/versions/__pycache__/3b741df497a3_add_company_dev_table.cpython-310.pyc
new file mode 100644
index 000000000..f6cfc52ca
Binary files /dev/null and b/lib/migrations/versions/__pycache__/3b741df497a3_add_company_dev_table.cpython-310.pyc differ
diff --git a/lib/migrations/versions/__pycache__/5f72c58bf48c_create_companies_devs.cpython-310.pyc b/lib/migrations/versions/__pycache__/5f72c58bf48c_create_companies_devs.cpython-310.pyc
new file mode 100644
index 000000000..088be7efa
Binary files /dev/null and b/lib/migrations/versions/__pycache__/5f72c58bf48c_create_companies_devs.cpython-310.pyc differ
diff --git a/lib/migrations/versions/__pycache__/7a71dbf71c64_create_db.cpython-310.pyc b/lib/migrations/versions/__pycache__/7a71dbf71c64_create_db.cpython-310.pyc
new file mode 100644
index 000000000..045631ee8
Binary files /dev/null and b/lib/migrations/versions/__pycache__/7a71dbf71c64_create_db.cpython-310.pyc differ
diff --git a/lib/migrations/versions/__pycache__/a3e1afdbcc19_add_freebies_table.cpython-310.pyc b/lib/migrations/versions/__pycache__/a3e1afdbcc19_add_freebies_table.cpython-310.pyc
new file mode 100644
index 000000000..400172bf1
Binary files /dev/null and b/lib/migrations/versions/__pycache__/a3e1afdbcc19_add_freebies_table.cpython-310.pyc differ
diff --git a/lib/migrations/versions/a3e1afdbcc19_add_freebies_table.py b/lib/migrations/versions/a3e1afdbcc19_add_freebies_table.py
new file mode 100644
index 000000000..78cc9b77d
--- /dev/null
+++ b/lib/migrations/versions/a3e1afdbcc19_add_freebies_table.py
@@ -0,0 +1,37 @@
+"""Add Freebies table
+
+Revision ID: a3e1afdbcc19
+Revises: 5f72c58bf48c
+Create Date: 2023-12-07 11:07:53.458094
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'a3e1afdbcc19'
+down_revision = '5f72c58bf48c'
+branch_labels = None
+depends_on = None
+
+
+def upgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('freebies',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('item_name', sa.String(), nullable=True),
+ sa.Column('value', sa.Integer(), nullable=True),
+ sa.Column('company_id', sa.Integer(), nullable=True),
+ sa.Column('dev_id', sa.Integer(), nullable=True),
+ sa.ForeignKeyConstraint(['company_id'], ['companies.id'], name=op.f('fk_freebies_company_id_companies')),
+ sa.ForeignKeyConstraint(['dev_id'], ['devs.id'], name=op.f('fk_freebies_dev_id_devs')),
+ sa.PrimaryKeyConstraint('id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('freebies')
+ # ### end Alembic commands ###
diff --git a/lib/models.py b/lib/models.py
index 2681bee5a..cbf7000c8 100644
--- a/lib/models.py
+++ b/lib/models.py
@@ -1,5 +1,6 @@
-from sqlalchemy import ForeignKey, Column, Integer, String, MetaData
-from sqlalchemy.orm import relationship, backref
+# models.py
+from sqlalchemy import Table, ForeignKey, Column, Integer, String, MetaData, create_engine
+from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
convention = {
@@ -18,12 +19,112 @@ class Company(Base):
def __repr__(self):
return f''
+ freebie = relationship('Freebie', backref='company')
+ devs = relationship('Dev', secondary='companies_devs', back_populates='companies')
+ def company_freebies(self):
+ return self.freebie
+ #session.query(Freebie).filter_by(company_id=self.id).all()
+ def company_devs(self):
+ return self.devs
+ def give_freebie(self,dev, item_name, value):
+ new_freebie=Freebie(dev=dev,company=self,item_name=item_name, value=value)
+ self.add(new_freebie)
+ session.commit()
+ @classmethod
+ def oldest_company(cls):
+ return cls.query.order_by(cls.founding_year).first()
+
+
+
class Dev(Base):
__tablename__ = 'devs'
id = Column(Integer(), primary_key=True)
- name= Column(String())
+ name = Column(String())
def __repr__(self):
return f''
+ freebie = relationship('Freebie', backref='dev')
+ companies = relationship('Company', secondary='companies_devs', back_populates='devs')
+
+ def dev_freebies(self):
+ return self.freebie
+ #session.query(Freebie).filter_by(dev_id=self.id).all()
+ def dev_companies(self):
+ return self.companies
+
+ def received_one(self, item_name):
+ return any(freebie.item_name == item_name for freebie in self.freebies)
+
+ def give_away(self, other_dev, freebie):
+ if freebie.dev == self:
+ freebie.dev = other_dev
+ session.commit()
+
+
+class Freebie(Base):
+ __tablename__ = 'freebies'
+
+ id = Column(Integer(), primary_key=True)
+ item_name = Column(String())
+ value = Column(Integer())
+ company_id = Column(Integer(), ForeignKey('companies.id'))
+ dev_id = Column(Integer(), ForeignKey('devs.id'))
+
+ def dev_instance_for_freebie(self):
+ return self.dev.name
+ def company_instance_for_freebie(self):
+ return self.company.name
+ def print_details(self):
+ return f"{self.dev.name} owns a {self.item_name} from {self.company.name}"
+
+
+
+company_dev= Table(
+ 'companies_devs',
+ Base.metadata,
+ Column('company_id', ForeignKey('companies.id'), primary_key=True),
+ Column('dev_id', ForeignKey('devs.id'), primary_key=True),
+ extend_existing=True,
+)
+
+engine = create_engine('sqlite:///freebies.db')
+Base.metadata.create_all(engine)
+
+Session = sessionmaker(bind=engine)
+session = Session()
+
+company4=session.query(Company).get(4)
+company4_freebies=company4.company_freebies()
+for freebie in company4_freebies:
+ print(f"Company 4 Freebies: {freebie.item_name}")
+
+company3=session.query(Company).get(3)
+company3_devs=company3.company_devs()
+for dev in company3_devs:
+ print(f"Company 3 devs:{dev.name}")
+
+dev4=session.query(Dev).get(4)
+dev4_freebies=dev4.dev_freebies()
+for freebie in dev4_freebies:
+ print(f'Dev 4 Freebies:{freebie.item_name}')
+
+dev5=session.query(Dev).get(5)
+dev5_company=dev5.dev_companies()
+for dev in dev5_company:
+ print(f"Developer 5 Company:{dev.name}")
+
+freebie4=session.query(Freebie).get(4)
+freebie4_dev=freebie4.dev_instance_for_freebie()
+print(f"Dev who got freebie4:{freebie4_dev}")
+
+freebie1=session.query(Freebie).get(9)
+freebie1_company=freebie1.company_instance_for_freebie()
+print(f'Freebie name:{freebie1_company}')
+
+
+print_details_instance =session.query(Freebie).get(3)
+details_string = print_details_instance.print_details()
+print(details_string)
+
diff --git a/lib/seed.py b/lib/seed.py
index b16becbbb..3f9b40ae1 100644
--- a/lib/seed.py
+++ b/lib/seed.py
@@ -1,3 +1,189 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
+#seed.py
+from models import Company,Dev,Freebie,company_dev,session
# Script goes here!
+session.query(Company).delete()
+session.query(Dev).delete()
+session.query(Freebie).delete()
+session.query(company_dev).delete()
+session.commit()
+companies_info=[{
+ "id": 1,
+ "name": "Devcast",
+ "founding_year": 1993
+}, {
+ "id": 2,
+ "name": "Kwinu",
+ "founding_year": 2010
+}, {
+ "id": 3,
+ "name": "Gigashots",
+ "founding_year": 2008
+}, {
+ "id": 4,
+ "name": "Voonder",
+ "founding_year": 1984
+}, {
+ "id": 5,
+ "name": "Chatterpoint",
+ "founding_year": 1992
+}, {
+ "id": 6,
+ "name": "Eabox",
+ "founding_year": 1998
+}, {
+ "id": 7,
+ "name": "Zoovu",
+ "founding_year": 2005
+}, {
+ "id": 8,
+ "name": "Zava",
+ "founding_year": 1987
+}, {
+ "id": 9,
+ "name": "Vitz",
+ "founding_year": 2011
+}, {
+ "id": 10,
+ "name": "Kazu",
+ "founding_year": 2004
+}]
+
+session.add_all([Company(**company) for company in companies_info])
+session.commit()
+
+devs_info=[{
+ "id": 1,
+ "name": "Ingaborg Maher"
+}, {
+ "id": 2,
+ "name": "Elijah Shiel"
+}, {
+ "id": 3,
+ "name": "Isidro Axford"
+}, {
+ "id": 4,
+ "name": "Lou Volleth"
+}, {
+ "id": 5,
+ "name": "Regan Jermin"
+}, {
+ "id": 6,
+ "name": "Kristel Fawdery"
+}, {
+ "id": 7,
+ "name": "Sonnnie Rubinowicz"
+}, {
+ "id": 8,
+ "name": "Bobette Beaufoy"
+}, {
+ "id": 9,
+ "name": "Maximo Elderton"
+}, {
+ "id": 10,
+ "name": "Laurianne Kilgrove"
+}]
+
+session.add_all([Dev(**dev) for dev in devs_info])
+session.commit()
+
+freebie_info=[
+ {
+ "id": 1,
+ "item_name": "Laptop",
+ "value": 1500,
+ "company_id": 3,
+ "dev_id": 8
+ },
+ {
+ "id": 2,
+ "item_name": "Mouse",
+ "value": 20,
+ "company_id": 5,
+ "dev_id": 2
+ },
+ {
+ "id": 3,
+ "item_name": "Keyboard",
+ "value": 50,
+ "company_id": 7,
+ "dev_id": 6
+ },
+ {
+ "id": 4,
+ "item_name": "USB Drive",
+ "value": 10,
+ "company_id": 1,
+ "dev_id": 9
+ },
+ {
+ "id": 5,
+ "item_name": "Headphones",
+ "value": 30,
+ "company_id": 9,
+ "dev_id": 4
+ },
+ {
+ "id": 6,
+ "item_name": "Webcam",
+ "value": 40,
+ "company_id": 2,
+ "dev_id": 7
+ },
+ {
+ "id": 7,
+ "item_name": "External Hard Drive",
+ "value": 80,
+ "company_id": 8,
+ "dev_id": 1
+ },
+ {
+ "id": 8,
+ "item_name": "Monitor",
+ "value": 200,
+ "company_id": 4,
+ "dev_id": 10
+ },
+ {
+ "id": 9,
+ "item_name": "Wireless Mouse",
+ "value": 25,
+ "company_id": 6,
+ "dev_id": 5
+ },
+ {
+ "id": 10,
+ "item_name": "Graphics Card",
+ "value": 300,
+ "company_id": 10,
+ "dev_id": 3
+ }
+]
+session.add_all([Freebie(**freebie) for freebie in freebie_info])
+session.commit()
+
+rls=[
+ {"id": 1, "company_id": 3, "dev_id": 8},
+ {"id": 2, "company_id": 7, "dev_id": 5},
+ {"id": 3, "company_id": 2, "dev_id": 7},
+ {"id": 4, "company_id": 6, "dev_id": 1},
+ {"id": 5, "company_id": 9, "dev_id": 4},
+ {"id": 6, "company_id": 1, "dev_id": 10},
+ {"id": 7, "company_id": 4, "dev_id": 2},
+ {"id": 8, "company_id": 8, "dev_id": 9},
+ {"id": 9, "company_id": 5, "dev_id": 3},
+ {"id": 10, "company_id": 10, "dev_id": 6}
+]
+dev1=session.query(Dev).filter_by(id=7).first()
+dev1.companies.append(session.query(Company).filter_by(id=2).first())
+dev2=session.query(Dev).filter_by(id=8).first()
+dev2.companies.append(session.query(Company).filter_by(id=3).first())
+dev3=session.query(Dev).filter_by(id=5).first()
+dev3.companies.append(session.query(Company).filter_by(id=7).first())
+dev4=session.query(Dev).filter_by(id=4).first()
+dev4.companies.append(session.query(Company).filter_by(id=9).first())
+dev5=session.query(Dev).filter_by(id=10).first()
+dev5.companies.append(session.query(Company).filter_by(id=1).first())
+session.commit()
+
diff --git a/migrations/README b/migrations/README
new file mode 100644
index 000000000..98e4f9c44
--- /dev/null
+++ b/migrations/README
@@ -0,0 +1 @@
+Generic single-database configuration.
\ No newline at end of file
diff --git a/migrations/__pycache__/env.cpython-310.pyc b/migrations/__pycache__/env.cpython-310.pyc
new file mode 100644
index 000000000..a2e18ef01
Binary files /dev/null and b/migrations/__pycache__/env.cpython-310.pyc differ
diff --git a/migrations/env.py b/migrations/env.py
new file mode 100644
index 000000000..6626bfd0c
--- /dev/null
+++ b/migrations/env.py
@@ -0,0 +1,78 @@
+from logging.config import fileConfig
+
+from sqlalchemy import engine_from_config
+from sqlalchemy import pool
+
+from alembic import context
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+if config.config_file_name is not None:
+ fileConfig(config.config_file_name)
+
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+target_metadata = None
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+
+def run_migrations_offline() -> None:
+ """Run migrations in 'offline' mode.
+
+ This configures the context with just a URL
+ and not an Engine, though an Engine is acceptable
+ here as well. By skipping the Engine creation
+ we don't even need a DBAPI to be available.
+
+ Calls to context.execute() here emit the given string to the
+ script output.
+
+ """
+ url = config.get_main_option("sqlalchemy.url")
+ context.configure(
+ url=url,
+ target_metadata=target_metadata,
+ literal_binds=True,
+ dialect_opts={"paramstyle": "named"},
+ )
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+def run_migrations_online() -> None:
+ """Run migrations in 'online' mode.
+
+ In this scenario we need to create an Engine
+ and associate a connection with the context.
+
+ """
+ connectable = engine_from_config(
+ config.get_section(config.config_ini_section),
+ prefix="sqlalchemy.",
+ poolclass=pool.NullPool,
+ )
+
+ with connectable.connect() as connection:
+ context.configure(
+ connection=connection, target_metadata=target_metadata
+ )
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+if context.is_offline_mode():
+ run_migrations_offline()
+else:
+ run_migrations_online()
diff --git a/migrations/script.py.mako b/migrations/script.py.mako
new file mode 100644
index 000000000..55df2863d
--- /dev/null
+++ b/migrations/script.py.mako
@@ -0,0 +1,24 @@
+"""${message}
+
+Revision ID: ${up_revision}
+Revises: ${down_revision | comma,n}
+Create Date: ${create_date}
+
+"""
+from alembic import op
+import sqlalchemy as sa
+${imports if imports else ""}
+
+# revision identifiers, used by Alembic.
+revision = ${repr(up_revision)}
+down_revision = ${repr(down_revision)}
+branch_labels = ${repr(branch_labels)}
+depends_on = ${repr(depends_on)}
+
+
+def upgrade() -> None:
+ ${upgrades if upgrades else "pass"}
+
+
+def downgrade() -> None:
+ ${downgrades if downgrades else "pass"}
diff --git a/venv/bin/Activate.ps1 b/venv/bin/Activate.ps1
new file mode 100644
index 000000000..b49d77ba4
--- /dev/null
+++ b/venv/bin/Activate.ps1
@@ -0,0 +1,247 @@
+<#
+.Synopsis
+Activate a Python virtual environment for the current PowerShell session.
+
+.Description
+Pushes the python executable for a virtual environment to the front of the
+$Env:PATH environment variable and sets the prompt to signify that you are
+in a Python virtual environment. Makes use of the command line switches as
+well as the `pyvenv.cfg` file values present in the virtual environment.
+
+.Parameter VenvDir
+Path to the directory that contains the virtual environment to activate. The
+default value for this is the parent of the directory that the Activate.ps1
+script is located within.
+
+.Parameter Prompt
+The prompt prefix to display when this virtual environment is activated. By
+default, this prompt is the name of the virtual environment folder (VenvDir)
+surrounded by parentheses and followed by a single space (ie. '(.venv) ').
+
+.Example
+Activate.ps1
+Activates the Python virtual environment that contains the Activate.ps1 script.
+
+.Example
+Activate.ps1 -Verbose
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and shows extra information about the activation as it executes.
+
+.Example
+Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
+Activates the Python virtual environment located in the specified location.
+
+.Example
+Activate.ps1 -Prompt "MyPython"
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and prefixes the current prompt with the specified string (surrounded in
+parentheses) while the virtual environment is active.
+
+.Notes
+On Windows, it may be required to enable this Activate.ps1 script by setting the
+execution policy for the user. You can do this by issuing the following PowerShell
+command:
+
+PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
+
+For more information on Execution Policies:
+https://go.microsoft.com/fwlink/?LinkID=135170
+
+#>
+Param(
+ [Parameter(Mandatory = $false)]
+ [String]
+ $VenvDir,
+ [Parameter(Mandatory = $false)]
+ [String]
+ $Prompt
+)
+
+<# Function declarations --------------------------------------------------- #>
+
+<#
+.Synopsis
+Remove all shell session elements added by the Activate script, including the
+addition of the virtual environment's Python executable from the beginning of
+the PATH variable.
+
+.Parameter NonDestructive
+If present, do not remove this function from the global namespace for the
+session.
+
+#>
+function global:deactivate ([switch]$NonDestructive) {
+ # Revert to original values
+
+ # The prior prompt:
+ if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
+ Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
+ Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
+ }
+
+ # The prior PYTHONHOME:
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
+ Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
+ Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
+ }
+
+ # The prior PATH:
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
+ Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
+ Remove-Item -Path Env:_OLD_VIRTUAL_PATH
+ }
+
+ # Just remove the VIRTUAL_ENV altogether:
+ if (Test-Path -Path Env:VIRTUAL_ENV) {
+ Remove-Item -Path env:VIRTUAL_ENV
+ }
+
+ # Just remove VIRTUAL_ENV_PROMPT altogether.
+ if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
+ Remove-Item -Path env:VIRTUAL_ENV_PROMPT
+ }
+
+ # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
+ if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
+ Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
+ }
+
+ # Leave deactivate function in the global namespace if requested:
+ if (-not $NonDestructive) {
+ Remove-Item -Path function:deactivate
+ }
+}
+
+<#
+.Description
+Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
+given folder, and returns them in a map.
+
+For each line in the pyvenv.cfg file, if that line can be parsed into exactly
+two strings separated by `=` (with any amount of whitespace surrounding the =)
+then it is considered a `key = value` line. The left hand string is the key,
+the right hand is the value.
+
+If the value starts with a `'` or a `"` then the first and last character is
+stripped from the value before being captured.
+
+.Parameter ConfigDir
+Path to the directory that contains the `pyvenv.cfg` file.
+#>
+function Get-PyVenvConfig(
+ [String]
+ $ConfigDir
+) {
+ Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
+
+ # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
+ $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
+
+ # An empty map will be returned if no config file is found.
+ $pyvenvConfig = @{ }
+
+ if ($pyvenvConfigPath) {
+
+ Write-Verbose "File exists, parse `key = value` lines"
+ $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
+
+ $pyvenvConfigContent | ForEach-Object {
+ $keyval = $PSItem -split "\s*=\s*", 2
+ if ($keyval[0] -and $keyval[1]) {
+ $val = $keyval[1]
+
+ # Remove extraneous quotations around a string value.
+ if ("'""".Contains($val.Substring(0, 1))) {
+ $val = $val.Substring(1, $val.Length - 2)
+ }
+
+ $pyvenvConfig[$keyval[0]] = $val
+ Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
+ }
+ }
+ }
+ return $pyvenvConfig
+}
+
+
+<# Begin Activate script --------------------------------------------------- #>
+
+# Determine the containing directory of this script
+$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
+$VenvExecDir = Get-Item -Path $VenvExecPath
+
+Write-Verbose "Activation script is located in path: '$VenvExecPath'"
+Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
+Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
+
+# Set values required in priority: CmdLine, ConfigFile, Default
+# First, get the location of the virtual environment, it might not be
+# VenvExecDir if specified on the command line.
+if ($VenvDir) {
+ Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
+}
+else {
+ Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
+ $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
+ Write-Verbose "VenvDir=$VenvDir"
+}
+
+# Next, read the `pyvenv.cfg` file to determine any required value such
+# as `prompt`.
+$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
+
+# Next, set the prompt from the command line, or the config file, or
+# just use the name of the virtual environment folder.
+if ($Prompt) {
+ Write-Verbose "Prompt specified as argument, using '$Prompt'"
+}
+else {
+ Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
+ if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
+ Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
+ $Prompt = $pyvenvCfg['prompt'];
+ }
+ else {
+ Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
+ Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
+ $Prompt = Split-Path -Path $venvDir -Leaf
+ }
+}
+
+Write-Verbose "Prompt = '$Prompt'"
+Write-Verbose "VenvDir='$VenvDir'"
+
+# Deactivate any currently active virtual environment, but leave the
+# deactivate function in place.
+deactivate -nondestructive
+
+# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
+# that there is an activated venv.
+$env:VIRTUAL_ENV = $VenvDir
+
+if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
+
+ Write-Verbose "Setting prompt to '$Prompt'"
+
+ # Set the prompt to include the env name
+ # Make sure _OLD_VIRTUAL_PROMPT is global
+ function global:_OLD_VIRTUAL_PROMPT { "" }
+ Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
+ New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
+
+ function global:prompt {
+ Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
+ _OLD_VIRTUAL_PROMPT
+ }
+ $env:VIRTUAL_ENV_PROMPT = $Prompt
+}
+
+# Clear PYTHONHOME
+if (Test-Path -Path Env:PYTHONHOME) {
+ Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
+ Remove-Item -Path Env:PYTHONHOME
+}
+
+# Add the venv to the PATH
+Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
+$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
diff --git a/venv/bin/activate b/venv/bin/activate
new file mode 100644
index 000000000..67c8646a7
--- /dev/null
+++ b/venv/bin/activate
@@ -0,0 +1,69 @@
+# This file must be used with "source bin/activate" *from bash*
+# you cannot run it directly
+
+deactivate () {
+ # reset old environment variables
+ if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
+ PATH="${_OLD_VIRTUAL_PATH:-}"
+ export PATH
+ unset _OLD_VIRTUAL_PATH
+ fi
+ if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
+ PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
+ export PYTHONHOME
+ unset _OLD_VIRTUAL_PYTHONHOME
+ fi
+
+ # This should detect bash and zsh, which have a hash command that must
+ # be called to get it to forget past commands. Without forgetting
+ # past commands the $PATH changes we made may not be respected
+ if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
+ hash -r 2> /dev/null
+ fi
+
+ if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
+ PS1="${_OLD_VIRTUAL_PS1:-}"
+ export PS1
+ unset _OLD_VIRTUAL_PS1
+ fi
+
+ unset VIRTUAL_ENV
+ unset VIRTUAL_ENV_PROMPT
+ if [ ! "${1:-}" = "nondestructive" ] ; then
+ # Self destruct!
+ unset -f deactivate
+ fi
+}
+
+# unset irrelevant variables
+deactivate nondestructive
+
+VIRTUAL_ENV="/home/angela/Development/code/python/python-p3-freebie-tracker/venv"
+export VIRTUAL_ENV
+
+_OLD_VIRTUAL_PATH="$PATH"
+PATH="$VIRTUAL_ENV/bin:$PATH"
+export PATH
+
+# unset PYTHONHOME if set
+# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
+# could use `if (set -u; : $PYTHONHOME) ;` in bash
+if [ -n "${PYTHONHOME:-}" ] ; then
+ _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
+ unset PYTHONHOME
+fi
+
+if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
+ _OLD_VIRTUAL_PS1="${PS1:-}"
+ PS1="(venv) ${PS1:-}"
+ export PS1
+ VIRTUAL_ENV_PROMPT="(venv) "
+ export VIRTUAL_ENV_PROMPT
+fi
+
+# This should detect bash and zsh, which have a hash command that must
+# be called to get it to forget past commands. Without forgetting
+# past commands the $PATH changes we made may not be respected
+if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
+ hash -r 2> /dev/null
+fi
diff --git a/venv/bin/activate.csh b/venv/bin/activate.csh
new file mode 100644
index 000000000..4b5b6a5b1
--- /dev/null
+++ b/venv/bin/activate.csh
@@ -0,0 +1,26 @@
+# This file must be used with "source bin/activate.csh" *from csh*.
+# You cannot run it directly.
+# Created by Davide Di Blasi .
+# Ported to Python 3.3 venv by Andrew Svetlov
+
+alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+setenv VIRTUAL_ENV "/home/angela/Development/code/python/python-p3-freebie-tracker/venv"
+
+set _OLD_VIRTUAL_PATH="$PATH"
+setenv PATH "$VIRTUAL_ENV/bin:$PATH"
+
+
+set _OLD_VIRTUAL_PROMPT="$prompt"
+
+if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
+ set prompt = "(venv) $prompt"
+ setenv VIRTUAL_ENV_PROMPT "(venv) "
+endif
+
+alias pydoc python -m pydoc
+
+rehash
diff --git a/venv/bin/activate.fish b/venv/bin/activate.fish
new file mode 100644
index 000000000..9b4037771
--- /dev/null
+++ b/venv/bin/activate.fish
@@ -0,0 +1,69 @@
+# This file must be used with "source /bin/activate.fish" *from fish*
+# (https://fishshell.com/); you cannot run it directly.
+
+function deactivate -d "Exit virtual environment and return to normal shell environment"
+ # reset old environment variables
+ if test -n "$_OLD_VIRTUAL_PATH"
+ set -gx PATH $_OLD_VIRTUAL_PATH
+ set -e _OLD_VIRTUAL_PATH
+ end
+ if test -n "$_OLD_VIRTUAL_PYTHONHOME"
+ set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
+ set -e _OLD_VIRTUAL_PYTHONHOME
+ end
+
+ if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
+ set -e _OLD_FISH_PROMPT_OVERRIDE
+ # prevents error when using nested fish instances (Issue #93858)
+ if functions -q _old_fish_prompt
+ functions -e fish_prompt
+ functions -c _old_fish_prompt fish_prompt
+ functions -e _old_fish_prompt
+ end
+ end
+
+ set -e VIRTUAL_ENV
+ set -e VIRTUAL_ENV_PROMPT
+ if test "$argv[1]" != "nondestructive"
+ # Self-destruct!
+ functions -e deactivate
+ end
+end
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+set -gx VIRTUAL_ENV "/home/angela/Development/code/python/python-p3-freebie-tracker/venv"
+
+set -gx _OLD_VIRTUAL_PATH $PATH
+set -gx PATH "$VIRTUAL_ENV/bin" $PATH
+
+# Unset PYTHONHOME if set.
+if set -q PYTHONHOME
+ set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
+ set -e PYTHONHOME
+end
+
+if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
+ # fish uses a function instead of an env var to generate the prompt.
+
+ # Save the current fish_prompt function as the function _old_fish_prompt.
+ functions -c fish_prompt _old_fish_prompt
+
+ # With the original prompt function renamed, we can override with our own.
+ function fish_prompt
+ # Save the return status of the last command.
+ set -l old_status $status
+
+ # Output the venv prompt; color taken from the blue of the Python logo.
+ printf "%s%s%s" (set_color 4B8BBE) "(venv) " (set_color normal)
+
+ # Restore the return status of the previous command.
+ echo "exit $old_status" | .
+ # Output the original/"old" prompt.
+ _old_fish_prompt
+ end
+
+ set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
+ set -gx VIRTUAL_ENV_PROMPT "(venv) "
+end
diff --git a/venv/bin/alembic b/venv/bin/alembic
new file mode 100755
index 000000000..583176158
--- /dev/null
+++ b/venv/bin/alembic
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python
+# -*- coding: utf-8 -*-
+import re
+import sys
+from alembic.config import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/venv/bin/ipdb3 b/venv/bin/ipdb3
new file mode 100755
index 000000000..b0bd41ef9
--- /dev/null
+++ b/venv/bin/ipdb3
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python
+# -*- coding: utf-8 -*-
+import re
+import sys
+from ipdb.__main__ import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/venv/bin/ipython b/venv/bin/ipython
new file mode 100755
index 000000000..cc3155d3a
--- /dev/null
+++ b/venv/bin/ipython
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python
+# -*- coding: utf-8 -*-
+import re
+import sys
+from IPython import start_ipython
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(start_ipython())
diff --git a/venv/bin/ipython3 b/venv/bin/ipython3
new file mode 100755
index 000000000..cc3155d3a
--- /dev/null
+++ b/venv/bin/ipython3
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python
+# -*- coding: utf-8 -*-
+import re
+import sys
+from IPython import start_ipython
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(start_ipython())
diff --git a/venv/bin/mako-render b/venv/bin/mako-render
new file mode 100755
index 000000000..e87525ce1
--- /dev/null
+++ b/venv/bin/mako-render
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python
+# -*- coding: utf-8 -*-
+import re
+import sys
+from mako.cmd import cmdline
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(cmdline())
diff --git a/venv/bin/pip b/venv/bin/pip
new file mode 100755
index 000000000..1916d37ae
--- /dev/null
+++ b/venv/bin/pip
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/venv/bin/pip3 b/venv/bin/pip3
new file mode 100755
index 000000000..1916d37ae
--- /dev/null
+++ b/venv/bin/pip3
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/venv/bin/pip3.10 b/venv/bin/pip3.10
new file mode 100755
index 000000000..1916d37ae
--- /dev/null
+++ b/venv/bin/pip3.10
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/venv/bin/pipenv b/venv/bin/pipenv
new file mode 100755
index 000000000..b7c6a455a
--- /dev/null
+++ b/venv/bin/pipenv
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pipenv import cli
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(cli())
diff --git a/venv/bin/pipenv-resolver b/venv/bin/pipenv-resolver
new file mode 100755
index 000000000..8b663cca9
--- /dev/null
+++ b/venv/bin/pipenv-resolver
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pipenv.resolver import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/venv/bin/pygmentize b/venv/bin/pygmentize
new file mode 100755
index 000000000..291a2479c
--- /dev/null
+++ b/venv/bin/pygmentize
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pygments.cmdline import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/venv/bin/python b/venv/bin/python
new file mode 120000
index 000000000..b8a0adbbb
--- /dev/null
+++ b/venv/bin/python
@@ -0,0 +1 @@
+python3
\ No newline at end of file
diff --git a/venv/bin/python3 b/venv/bin/python3
new file mode 120000
index 000000000..ae65fdaa1
--- /dev/null
+++ b/venv/bin/python3
@@ -0,0 +1 @@
+/usr/bin/python3
\ No newline at end of file
diff --git a/venv/bin/python3.10 b/venv/bin/python3.10
new file mode 120000
index 000000000..b8a0adbbb
--- /dev/null
+++ b/venv/bin/python3.10
@@ -0,0 +1 @@
+python3
\ No newline at end of file
diff --git a/venv/bin/virtualenv b/venv/bin/virtualenv
new file mode 100755
index 000000000..b513b66ea
--- /dev/null
+++ b/venv/bin/virtualenv
@@ -0,0 +1,8 @@
+#!/home/angela/Development/code/python/python-p3-freebie-tracker/venv/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from virtualenv.__main__ import run_with_catch
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(run_with_catch())
diff --git a/venv/include/site/python3.10/greenlet/greenlet.h b/venv/include/site/python3.10/greenlet/greenlet.h
new file mode 100644
index 000000000..d02a16e43
--- /dev/null
+++ b/venv/include/site/python3.10/greenlet/greenlet.h
@@ -0,0 +1,164 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+
+/* Greenlet object interface */
+
+#ifndef Py_GREENLETOBJECT_H
+#define Py_GREENLETOBJECT_H
+
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is deprecated and undocumented. It does not change. */
+#define GREENLET_VERSION "1.0.0"
+
+#ifndef GREENLET_MODULE
+#define implementation_ptr_t void*
+#endif
+
+typedef struct _greenlet {
+ PyObject_HEAD
+ PyObject* weakreflist;
+ PyObject* dict;
+ implementation_ptr_t pimpl;
+} PyGreenlet;
+
+#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
+
+
+/* C API functions */
+
+/* Total number of symbols that are exported */
+#define PyGreenlet_API_pointers 12
+
+#define PyGreenlet_Type_NUM 0
+#define PyExc_GreenletError_NUM 1
+#define PyExc_GreenletExit_NUM 2
+
+#define PyGreenlet_New_NUM 3
+#define PyGreenlet_GetCurrent_NUM 4
+#define PyGreenlet_Throw_NUM 5
+#define PyGreenlet_Switch_NUM 6
+#define PyGreenlet_SetParent_NUM 7
+
+#define PyGreenlet_MAIN_NUM 8
+#define PyGreenlet_STARTED_NUM 9
+#define PyGreenlet_ACTIVE_NUM 10
+#define PyGreenlet_GET_PARENT_NUM 11
+
+#ifndef GREENLET_MODULE
+/* This section is used by modules that uses the greenlet C API */
+static void** _PyGreenlet_API = NULL;
+
+# define PyGreenlet_Type \
+ (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
+
+# define PyExc_GreenletError \
+ ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
+
+# define PyExc_GreenletExit \
+ ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
+
+/*
+ * PyGreenlet_New(PyObject *args)
+ *
+ * greenlet.greenlet(run, parent=None)
+ */
+# define PyGreenlet_New \
+ (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
+ _PyGreenlet_API[PyGreenlet_New_NUM])
+
+/*
+ * PyGreenlet_GetCurrent(void)
+ *
+ * greenlet.getcurrent()
+ */
+# define PyGreenlet_GetCurrent \
+ (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
+
+/*
+ * PyGreenlet_Throw(
+ * PyGreenlet *greenlet,
+ * PyObject *typ,
+ * PyObject *val,
+ * PyObject *tb)
+ *
+ * g.throw(...)
+ */
+# define PyGreenlet_Throw \
+ (*(PyObject * (*)(PyGreenlet * self, \
+ PyObject * typ, \
+ PyObject * val, \
+ PyObject * tb)) \
+ _PyGreenlet_API[PyGreenlet_Throw_NUM])
+
+/*
+ * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
+ *
+ * g.switch(*args, **kwargs)
+ */
+# define PyGreenlet_Switch \
+ (*(PyObject * \
+ (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
+ _PyGreenlet_API[PyGreenlet_Switch_NUM])
+
+/*
+ * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
+ *
+ * g.parent = new_parent
+ */
+# define PyGreenlet_SetParent \
+ (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
+ _PyGreenlet_API[PyGreenlet_SetParent_NUM])
+
+/*
+ * PyGreenlet_GetParent(PyObject* greenlet)
+ *
+ * return greenlet.parent;
+ *
+ * This could return NULL even if there is no exception active.
+ * If it does not return NULL, you are responsible for decrementing the
+ * reference count.
+ */
+# define PyGreenlet_GetParent \
+ (*(PyGreenlet* (*)(PyGreenlet*)) \
+ _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
+
+/*
+ * deprecated, undocumented alias.
+ */
+# define PyGreenlet_GET_PARENT PyGreenlet_GetParent
+
+# define PyGreenlet_MAIN \
+ (*(int (*)(PyGreenlet*)) \
+ _PyGreenlet_API[PyGreenlet_MAIN_NUM])
+
+# define PyGreenlet_STARTED \
+ (*(int (*)(PyGreenlet*)) \
+ _PyGreenlet_API[PyGreenlet_STARTED_NUM])
+
+# define PyGreenlet_ACTIVE \
+ (*(int (*)(PyGreenlet*)) \
+ _PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
+
+
+
+
+/* Macro that imports greenlet and initializes C API */
+/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
+ keep the older definition to be sure older code that might have a copy of
+ the header still works. */
+# define PyGreenlet_Import() \
+ { \
+ _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
+ }
+
+#endif /* GREENLET_MODULE */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_GREENLETOBJECT_H */
diff --git a/venv/lib/python3.10/site-packages/IPython/__init__.py b/venv/lib/python3.10/site-packages/IPython/__init__.py
new file mode 100644
index 000000000..c224f9a8c
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/__init__.py
@@ -0,0 +1,156 @@
+# PYTHON_ARGCOMPLETE_OK
+"""
+IPython: tools for interactive and parallel computing in Python.
+
+https://ipython.org
+"""
+#-----------------------------------------------------------------------------
+# Copyright (c) 2008-2011, IPython Development Team.
+# Copyright (c) 2001-2007, Fernando Perez
+# Copyright (c) 2001, Janko Hauser
+# Copyright (c) 2001, Nathaniel Gray
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+
+import sys
+
+#-----------------------------------------------------------------------------
+# Setup everything
+#-----------------------------------------------------------------------------
+
+# Don't forget to also update setup.py when this changes!
+if sys.version_info < (3, 8):
+ raise ImportError(
+ """
+IPython 8+ supports Python 3.8 and above, following NEP 29.
+When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
+Python 3.3 and 3.4 were supported up to IPython 6.x.
+Python 3.5 was supported with IPython 7.0 to 7.9.
+Python 3.6 was supported with IPython up to 7.16.
+Python 3.7 was still supported with the 7.x branch.
+
+See IPython `README.rst` file for more information:
+
+ https://github.com/ipython/ipython/blob/main/README.rst
+
+"""
+ )
+
+#-----------------------------------------------------------------------------
+# Setup the top level names
+#-----------------------------------------------------------------------------
+
+from .core.getipython import get_ipython
+from .core import release
+from .core.application import Application
+from .terminal.embed import embed
+
+from .core.interactiveshell import InteractiveShell
+from .utils.sysinfo import sys_info
+from .utils.frame import extract_module_locals
+
+# Release data
+__author__ = '%s <%s>' % (release.author, release.author_email)
+__license__ = release.license
+__version__ = release.version
+version_info = release.version_info
+# list of CVEs that should have been patched in this release.
+# this is informational and should not be relied upon.
+__patched_cves__ = {"CVE-2022-21699"}
+
+
+def embed_kernel(module=None, local_ns=None, **kwargs):
+ """Embed and start an IPython kernel in a given scope.
+
+ If you don't want the kernel to initialize the namespace
+ from the scope of the surrounding function,
+ and/or you want to load full IPython configuration,
+ you probably want `IPython.start_kernel()` instead.
+
+ Parameters
+ ----------
+ module : types.ModuleType, optional
+ The module to load into IPython globals (default: caller)
+ local_ns : dict, optional
+ The namespace to load into IPython user namespace (default: caller)
+ **kwargs : various, optional
+ Further keyword args are relayed to the IPKernelApp constructor,
+ allowing configuration of the Kernel. Will only have an effect
+ on the first embed_kernel call for a given process.
+ """
+
+ (caller_module, caller_locals) = extract_module_locals(1)
+ if module is None:
+ module = caller_module
+ if local_ns is None:
+ local_ns = caller_locals
+
+ # Only import .zmq when we really need it
+ from ipykernel.embed import embed_kernel as real_embed_kernel
+ real_embed_kernel(module=module, local_ns=local_ns, **kwargs)
+
+def start_ipython(argv=None, **kwargs):
+ """Launch a normal IPython instance (as opposed to embedded)
+
+ `IPython.embed()` puts a shell in a particular calling scope,
+ such as a function or method for debugging purposes,
+ which is often not desirable.
+
+ `start_ipython()` does full, regular IPython initialization,
+ including loading startup files, configuration, etc.
+ much of which is skipped by `embed()`.
+
+ This is a public API method, and will survive implementation changes.
+
+ Parameters
+ ----------
+ argv : list or None, optional
+ If unspecified or None, IPython will parse command-line options from sys.argv.
+ To prevent any command-line parsing, pass an empty list: `argv=[]`.
+ user_ns : dict, optional
+ specify this dictionary to initialize the IPython user namespace with particular values.
+ **kwargs : various, optional
+ Any other kwargs will be passed to the Application constructor,
+ such as `config`.
+ """
+ from IPython.terminal.ipapp import launch_new_instance
+ return launch_new_instance(argv=argv, **kwargs)
+
+def start_kernel(argv=None, **kwargs):
+ """Launch a normal IPython kernel instance (as opposed to embedded)
+
+ `IPython.embed_kernel()` puts a shell in a particular calling scope,
+ such as a function or method for debugging purposes,
+ which is often not desirable.
+
+ `start_kernel()` does full, regular IPython initialization,
+ including loading startup files, configuration, etc.
+ much of which is skipped by `embed()`.
+
+ Parameters
+ ----------
+ argv : list or None, optional
+ If unspecified or None, IPython will parse command-line options from sys.argv.
+ To prevent any command-line parsing, pass an empty list: `argv=[]`.
+ user_ns : dict, optional
+ specify this dictionary to initialize the IPython user namespace with particular values.
+ **kwargs : various, optional
+ Any other kwargs will be passed to the Application constructor,
+ such as `config`.
+ """
+ import warnings
+
+ warnings.warn(
+ "start_kernel is deprecated since IPython 8.0, use from `ipykernel.kernelapp.launch_new_instance`",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ from ipykernel.kernelapp import launch_new_instance
+ return launch_new_instance(argv=argv, **kwargs)
diff --git a/venv/lib/python3.10/site-packages/IPython/__main__.py b/venv/lib/python3.10/site-packages/IPython/__main__.py
new file mode 100644
index 000000000..8e9f989a8
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/__main__.py
@@ -0,0 +1,15 @@
+# PYTHON_ARGCOMPLETE_OK
+# encoding: utf-8
+"""Terminal-based IPython entry point.
+"""
+#-----------------------------------------------------------------------------
+# Copyright (c) 2012, IPython Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from IPython import start_ipython
+
+start_ipython()
diff --git a/venv/lib/python3.10/site-packages/IPython/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 000000000..fb0a29216
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/__pycache__/__main__.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/__pycache__/__main__.cpython-310.pyc
new file mode 100644
index 000000000..3521e8dd2
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/__pycache__/__main__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/__pycache__/conftest.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/__pycache__/conftest.cpython-310.pyc
new file mode 100644
index 000000000..ddd464c7b
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/__pycache__/conftest.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/__pycache__/consoleapp.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/__pycache__/consoleapp.cpython-310.pyc
new file mode 100644
index 000000000..b8efdd84a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/__pycache__/consoleapp.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/__pycache__/display.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/__pycache__/display.cpython-310.pyc
new file mode 100644
index 000000000..43a081302
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/__pycache__/display.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/__pycache__/paths.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/__pycache__/paths.cpython-310.pyc
new file mode 100644
index 000000000..af1d2bba0
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/__pycache__/paths.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/conftest.py b/venv/lib/python3.10/site-packages/IPython/conftest.py
new file mode 100644
index 000000000..abf613147
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/conftest.py
@@ -0,0 +1,87 @@
+import builtins
+import inspect
+import os
+import pathlib
+import shutil
+import sys
+import types
+
+import pytest
+
+# Must register before it gets imported
+pytest.register_assert_rewrite("IPython.testing.tools")
+
+from .testing import tools
+
+
+def pytest_collection_modifyitems(items):
+ """This function is automatically run by pytest passing all collected test
+ functions.
+
+ We use it to add asyncio marker to all async tests and assert we don't use
+ test functions that are async generators which wouldn't make sense.
+ """
+ for item in items:
+ if inspect.iscoroutinefunction(item.obj):
+ item.add_marker("asyncio")
+ assert not inspect.isasyncgenfunction(item.obj)
+
+
+def get_ipython():
+ from .terminal.interactiveshell import TerminalInteractiveShell
+ if TerminalInteractiveShell._instance:
+ return TerminalInteractiveShell.instance()
+
+ config = tools.default_config()
+ config.TerminalInteractiveShell.simple_prompt = True
+
+ # Create and initialize our test-friendly IPython instance.
+ shell = TerminalInteractiveShell.instance(config=config)
+ return shell
+
+
+@pytest.fixture(scope='session', autouse=True)
+def work_path():
+ path = pathlib.Path("./tmp-ipython-pytest-profiledir")
+ os.environ["IPYTHONDIR"] = str(path.absolute())
+ if path.exists():
+ raise ValueError('IPython dir temporary path already exists ! Did previous test run exit successfully ?')
+ path.mkdir()
+ yield
+ shutil.rmtree(str(path.resolve()))
+
+
+def nopage(strng, start=0, screen_lines=0, pager_cmd=None):
+ if isinstance(strng, dict):
+ strng = strng.get("text/plain", "")
+ print(strng)
+
+
+def xsys(self, cmd):
+ """Replace the default system call with a capturing one for doctest.
+ """
+ # We use getoutput, but we need to strip it because pexpect captures
+ # the trailing newline differently from commands.getoutput
+ print(self.getoutput(cmd, split=False, depth=1).rstrip(), end="", file=sys.stdout)
+ sys.stdout.flush()
+
+
+# for things to work correctly we would need this as a session fixture;
+# unfortunately this will fail on some test that get executed as _collection_
+# time (before the fixture run), in particular parametrized test that contain
+# yields. so for now execute at import time.
+#@pytest.fixture(autouse=True, scope='session')
+def inject():
+
+ builtins.get_ipython = get_ipython
+ builtins._ip = get_ipython()
+ builtins.ip = get_ipython()
+ builtins.ip.system = types.MethodType(xsys, ip)
+ builtins.ip.builtin_trap.activate()
+ from .core import page
+
+ page.pager_page = nopage
+ # yield
+
+
+inject()
diff --git a/venv/lib/python3.10/site-packages/IPython/consoleapp.py b/venv/lib/python3.10/site-packages/IPython/consoleapp.py
new file mode 100644
index 000000000..c2bbe1888
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/consoleapp.py
@@ -0,0 +1,12 @@
+"""
+Shim to maintain backwards compatibility with old IPython.consoleapp imports.
+"""
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+from warnings import warn
+
+warn("The `IPython.consoleapp` package has been deprecated since IPython 4.0."
+ "You should import from jupyter_client.consoleapp instead.", stacklevel=2)
+
+from jupyter_client.consoleapp import *
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__init__.py b/venv/lib/python3.10/site-packages/IPython/core/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 000000000..6beccff12
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/alias.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/alias.cpython-310.pyc
new file mode 100644
index 000000000..334f4c86c
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/alias.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/application.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/application.cpython-310.pyc
new file mode 100644
index 000000000..5add18eb4
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/application.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/async_helpers.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/async_helpers.cpython-310.pyc
new file mode 100644
index 000000000..1c6a86cdb
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/async_helpers.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/autocall.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/autocall.cpython-310.pyc
new file mode 100644
index 000000000..933785e4b
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/autocall.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/builtin_trap.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/builtin_trap.cpython-310.pyc
new file mode 100644
index 000000000..4daff07f5
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/builtin_trap.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/compilerop.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/compilerop.cpython-310.pyc
new file mode 100644
index 000000000..9c9acc6c6
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/compilerop.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/completer.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/completer.cpython-310.pyc
new file mode 100644
index 000000000..d356ff976
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/completer.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/completerlib.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/completerlib.cpython-310.pyc
new file mode 100644
index 000000000..ad2d1fdb8
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/completerlib.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/crashhandler.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/crashhandler.cpython-310.pyc
new file mode 100644
index 000000000..e13e1fd7a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/crashhandler.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/debugger.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/debugger.cpython-310.pyc
new file mode 100644
index 000000000..c52b7ae43
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/debugger.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/display.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/display.cpython-310.pyc
new file mode 100644
index 000000000..222a7d67a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/display.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/display_functions.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/display_functions.cpython-310.pyc
new file mode 100644
index 000000000..3824f0205
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/display_functions.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/display_trap.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/display_trap.cpython-310.pyc
new file mode 100644
index 000000000..3b51f973d
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/display_trap.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/displayhook.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/displayhook.cpython-310.pyc
new file mode 100644
index 000000000..c82b6dd71
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/displayhook.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/displaypub.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/displaypub.cpython-310.pyc
new file mode 100644
index 000000000..aaa928e82
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/displaypub.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/error.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/error.cpython-310.pyc
new file mode 100644
index 000000000..108c0147d
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/error.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/events.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/events.cpython-310.pyc
new file mode 100644
index 000000000..966a3a7d6
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/events.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/excolors.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/excolors.cpython-310.pyc
new file mode 100644
index 000000000..da4961076
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/excolors.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/extensions.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/extensions.cpython-310.pyc
new file mode 100644
index 000000000..88a6000a3
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/extensions.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/formatters.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/formatters.cpython-310.pyc
new file mode 100644
index 000000000..40af84325
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/formatters.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/getipython.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/getipython.cpython-310.pyc
new file mode 100644
index 000000000..829a81b8b
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/getipython.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/guarded_eval.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/guarded_eval.cpython-310.pyc
new file mode 100644
index 000000000..9605d86e6
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/guarded_eval.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/history.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/history.cpython-310.pyc
new file mode 100644
index 000000000..fe24fadbe
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/history.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/historyapp.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/historyapp.cpython-310.pyc
new file mode 100644
index 000000000..d44235a2a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/historyapp.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/hooks.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/hooks.cpython-310.pyc
new file mode 100644
index 000000000..3308d6da2
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/hooks.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/inputsplitter.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/inputsplitter.cpython-310.pyc
new file mode 100644
index 000000000..74fe7ef7f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/inputsplitter.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/inputtransformer.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/inputtransformer.cpython-310.pyc
new file mode 100644
index 000000000..aa6af75d1
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/inputtransformer.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/inputtransformer2.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/inputtransformer2.cpython-310.pyc
new file mode 100644
index 000000000..cd3799ea3
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/inputtransformer2.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/interactiveshell.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/interactiveshell.cpython-310.pyc
new file mode 100644
index 000000000..af1622b01
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/interactiveshell.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/latex_symbols.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/latex_symbols.cpython-310.pyc
new file mode 100644
index 000000000..61af5ea18
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/latex_symbols.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/logger.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/logger.cpython-310.pyc
new file mode 100644
index 000000000..5ba0e55bd
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/logger.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/macro.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/macro.cpython-310.pyc
new file mode 100644
index 000000000..76a1ed0aa
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/macro.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/magic.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/magic.cpython-310.pyc
new file mode 100644
index 000000000..fc958c1a8
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/magic.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/magic_arguments.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/magic_arguments.cpython-310.pyc
new file mode 100644
index 000000000..0ea5035b7
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/magic_arguments.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/oinspect.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/oinspect.cpython-310.pyc
new file mode 100644
index 000000000..d3f264f6d
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/oinspect.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/page.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/page.cpython-310.pyc
new file mode 100644
index 000000000..c12834a9f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/page.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/payload.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/payload.cpython-310.pyc
new file mode 100644
index 000000000..805df213a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/payload.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/payloadpage.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/payloadpage.cpython-310.pyc
new file mode 100644
index 000000000..143d04489
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/payloadpage.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/prefilter.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/prefilter.cpython-310.pyc
new file mode 100644
index 000000000..f203a4d7f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/prefilter.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/profileapp.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/profileapp.cpython-310.pyc
new file mode 100644
index 000000000..5d502eeb3
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/profileapp.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/profiledir.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/profiledir.cpython-310.pyc
new file mode 100644
index 000000000..1ec0d415e
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/profiledir.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/prompts.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/prompts.cpython-310.pyc
new file mode 100644
index 000000000..79cc40717
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/prompts.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/pylabtools.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/pylabtools.cpython-310.pyc
new file mode 100644
index 000000000..0a930ff5e
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/pylabtools.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/release.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/release.cpython-310.pyc
new file mode 100644
index 000000000..3b0eab29e
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/release.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/shellapp.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/shellapp.cpython-310.pyc
new file mode 100644
index 000000000..81d066e1d
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/shellapp.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/splitinput.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/splitinput.cpython-310.pyc
new file mode 100644
index 000000000..84cae2969
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/splitinput.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/ultratb.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/ultratb.cpython-310.pyc
new file mode 100644
index 000000000..88529befc
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/ultratb.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/__pycache__/usage.cpython-310.pyc b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/usage.cpython-310.pyc
new file mode 100644
index 000000000..fb1665b93
Binary files /dev/null and b/venv/lib/python3.10/site-packages/IPython/core/__pycache__/usage.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/IPython/core/alias.py b/venv/lib/python3.10/site-packages/IPython/core/alias.py
new file mode 100644
index 000000000..2ad990231
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/core/alias.py
@@ -0,0 +1,258 @@
+# encoding: utf-8
+"""
+System command aliases.
+
+Authors:
+
+* Fernando Perez
+* Brian Granger
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (C) 2008-2011 The IPython Development Team
+#
+# Distributed under the terms of the BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+
+import os
+import re
+import sys
+
+from traitlets.config.configurable import Configurable
+from .error import UsageError
+
+from traitlets import List, Instance
+from logging import error
+
+#-----------------------------------------------------------------------------
+# Utilities
+#-----------------------------------------------------------------------------
+
+# This is used as the pattern for calls to split_user_input.
+shell_line_split = re.compile(r'^(\s*)()(\S+)(.*$)')
+
+def default_aliases():
+ """Return list of shell aliases to auto-define.
+ """
+ # Note: the aliases defined here should be safe to use on a kernel
+ # regardless of what frontend it is attached to. Frontends that use a
+ # kernel in-process can define additional aliases that will only work in
+ # their case. For example, things like 'less' or 'clear' that manipulate
+ # the terminal should NOT be declared here, as they will only work if the
+ # kernel is running inside a true terminal, and not over the network.
+
+ if os.name == 'posix':
+ default_aliases = [('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
+ ('mv', 'mv'), ('rm', 'rm'), ('cp', 'cp'),
+ ('cat', 'cat'),
+ ]
+ # Useful set of ls aliases. The GNU and BSD options are a little
+ # different, so we make aliases that provide as similar as possible
+ # behavior in ipython, by passing the right flags for each platform
+ if sys.platform.startswith('linux'):
+ ls_aliases = [('ls', 'ls -F --color'),
+ # long ls
+ ('ll', 'ls -F -o --color'),
+ # ls normal files only
+ ('lf', 'ls -F -o --color %l | grep ^-'),
+ # ls symbolic links
+ ('lk', 'ls -F -o --color %l | grep ^l'),
+ # directories or links to directories,
+ ('ldir', 'ls -F -o --color %l | grep /$'),
+ # things which are executable
+ ('lx', 'ls -F -o --color %l | grep ^-..x'),
+ ]
+ elif sys.platform.startswith('openbsd') or sys.platform.startswith('netbsd'):
+ # OpenBSD, NetBSD. The ls implementation on these platforms do not support
+ # the -G switch and lack the ability to use colorized output.
+ ls_aliases = [('ls', 'ls -F'),
+ # long ls
+ ('ll', 'ls -F -l'),
+ # ls normal files only
+ ('lf', 'ls -F -l %l | grep ^-'),
+ # ls symbolic links
+ ('lk', 'ls -F -l %l | grep ^l'),
+ # directories or links to directories,
+ ('ldir', 'ls -F -l %l | grep /$'),
+ # things which are executable
+ ('lx', 'ls -F -l %l | grep ^-..x'),
+ ]
+ else:
+ # BSD, OSX, etc.
+ ls_aliases = [('ls', 'ls -F -G'),
+ # long ls
+ ('ll', 'ls -F -l -G'),
+ # ls normal files only
+ ('lf', 'ls -F -l -G %l | grep ^-'),
+ # ls symbolic links
+ ('lk', 'ls -F -l -G %l | grep ^l'),
+ # directories or links to directories,
+ ('ldir', 'ls -F -G -l %l | grep /$'),
+ # things which are executable
+ ('lx', 'ls -F -l -G %l | grep ^-..x'),
+ ]
+ default_aliases = default_aliases + ls_aliases
+ elif os.name in ['nt', 'dos']:
+ default_aliases = [('ls', 'dir /on'),
+ ('ddir', 'dir /ad /on'), ('ldir', 'dir /ad /on'),
+ ('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
+ ('echo', 'echo'), ('ren', 'ren'), ('copy', 'copy'),
+ ]
+ else:
+ default_aliases = []
+
+ return default_aliases
+
+
+class AliasError(Exception):
+ pass
+
+
+class InvalidAliasError(AliasError):
+ pass
+
+class Alias(object):
+ """Callable object storing the details of one alias.
+
+ Instances are registered as magic functions to allow use of aliases.
+ """
+
+ # Prepare blacklist
+ blacklist = {'cd','popd','pushd','dhist','alias','unalias'}
+
+ def __init__(self, shell, name, cmd):
+ self.shell = shell
+ self.name = name
+ self.cmd = cmd
+ self.__doc__ = "Alias for `!{}`".format(cmd)
+ self.nargs = self.validate()
+
+ def validate(self):
+ """Validate the alias, and return the number of arguments."""
+ if self.name in self.blacklist:
+ raise InvalidAliasError("The name %s can't be aliased "
+ "because it is a keyword or builtin." % self.name)
+ try:
+ caller = self.shell.magics_manager.magics['line'][self.name]
+ except KeyError:
+ pass
+ else:
+ if not isinstance(caller, Alias):
+ raise InvalidAliasError("The name %s can't be aliased "
+ "because it is another magic command." % self.name)
+
+ if not (isinstance(self.cmd, str)):
+ raise InvalidAliasError("An alias command must be a string, "
+ "got: %r" % self.cmd)
+
+ nargs = self.cmd.count('%s') - self.cmd.count('%%s')
+
+ if (nargs > 0) and (self.cmd.find('%l') >= 0):
+ raise InvalidAliasError('The %s and %l specifiers are mutually '
+ 'exclusive in alias definitions.')
+
+ return nargs
+
+ def __repr__(self):
+ return "".format(self.name, self.cmd)
+
+ def __call__(self, rest=''):
+ cmd = self.cmd
+ nargs = self.nargs
+ # Expand the %l special to be the user's input line
+ if cmd.find('%l') >= 0:
+ cmd = cmd.replace('%l', rest)
+ rest = ''
+
+ if nargs==0:
+ if cmd.find('%%s') >= 1:
+ cmd = cmd.replace('%%s', '%s')
+ # Simple, argument-less aliases
+ cmd = '%s %s' % (cmd, rest)
+ else:
+ # Handle aliases with positional arguments
+ args = rest.split(None, nargs)
+ if len(args) < nargs:
+ raise UsageError('Alias <%s> requires %s arguments, %s given.' %
+ (self.name, nargs, len(args)))
+ cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:]))
+
+ self.shell.system(cmd)
+
+#-----------------------------------------------------------------------------
+# Main AliasManager class
+#-----------------------------------------------------------------------------
+
+class AliasManager(Configurable):
+
+ default_aliases = List(default_aliases()).tag(config=True)
+ user_aliases = List(default_value=[]).tag(config=True)
+ shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
+
+ def __init__(self, shell=None, **kwargs):
+ super(AliasManager, self).__init__(shell=shell, **kwargs)
+ # For convenient access
+ self.linemagics = self.shell.magics_manager.magics['line']
+ self.init_aliases()
+
+ def init_aliases(self):
+ # Load default & user aliases
+ for name, cmd in self.default_aliases + self.user_aliases:
+ if cmd.startswith('ls ') and self.shell.colors == 'NoColor':
+ cmd = cmd.replace(' --color', '')
+ self.soft_define_alias(name, cmd)
+
+ @property
+ def aliases(self):
+ return [(n, func.cmd) for (n, func) in self.linemagics.items()
+ if isinstance(func, Alias)]
+
+ def soft_define_alias(self, name, cmd):
+ """Define an alias, but don't raise on an AliasError."""
+ try:
+ self.define_alias(name, cmd)
+ except AliasError as e:
+ error("Invalid alias: %s" % e)
+
+ def define_alias(self, name, cmd):
+ """Define a new alias after validating it.
+
+ This will raise an :exc:`AliasError` if there are validation
+ problems.
+ """
+ caller = Alias(shell=self.shell, name=name, cmd=cmd)
+ self.shell.magics_manager.register_function(caller, magic_kind='line',
+ magic_name=name)
+
+ def get_alias(self, name):
+ """Return an alias, or None if no alias by that name exists."""
+ aname = self.linemagics.get(name, None)
+ return aname if isinstance(aname, Alias) else None
+
+ def is_alias(self, name):
+ """Return whether or not a given name has been defined as an alias"""
+ return self.get_alias(name) is not None
+
+ def undefine_alias(self, name):
+ if self.is_alias(name):
+ del self.linemagics[name]
+ else:
+ raise ValueError('%s is not an alias' % name)
+
+ def clear_aliases(self):
+ for name, cmd in self.aliases:
+ self.undefine_alias(name)
+
+ def retrieve_alias(self, name):
+ """Retrieve the command to which an alias expands."""
+ caller = self.get_alias(name)
+ if caller:
+ return caller.cmd
+ else:
+ raise ValueError('%s is not an alias' % name)
diff --git a/venv/lib/python3.10/site-packages/IPython/core/application.py b/venv/lib/python3.10/site-packages/IPython/core/application.py
new file mode 100644
index 000000000..26c061661
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/core/application.py
@@ -0,0 +1,489 @@
+# encoding: utf-8
+"""
+An application for IPython.
+
+All top-level applications should use the classes in this module for
+handling configuration and creating configurables.
+
+The job of an :class:`Application` is to create the master configuration
+object and then create the configurable objects, passing the config to them.
+"""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import atexit
+from copy import deepcopy
+import logging
+import os
+import shutil
+import sys
+
+from pathlib import Path
+
+from traitlets.config.application import Application, catch_config_error
+from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader
+from IPython.core import release, crashhandler
+from IPython.core.profiledir import ProfileDir, ProfileDirError
+from IPython.paths import get_ipython_dir, get_ipython_package_dir
+from IPython.utils.path import ensure_dir_exists
+from traitlets import (
+ List, Unicode, Type, Bool, Set, Instance, Undefined,
+ default, observe,
+)
+
+if os.name == "nt":
+ programdata = os.environ.get("PROGRAMDATA", None)
+ if programdata is not None:
+ SYSTEM_CONFIG_DIRS = [str(Path(programdata) / "ipython")]
+ else: # PROGRAMDATA is not defined by default on XP.
+ SYSTEM_CONFIG_DIRS = []
+else:
+ SYSTEM_CONFIG_DIRS = [
+ "/usr/local/etc/ipython",
+ "/etc/ipython",
+ ]
+
+
+ENV_CONFIG_DIRS = []
+_env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython')
+if _env_config_dir not in SYSTEM_CONFIG_DIRS:
+ # only add ENV_CONFIG if sys.prefix is not already included
+ ENV_CONFIG_DIRS.append(_env_config_dir)
+
+
+_envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS')
+if _envvar in {None, ''}:
+ IPYTHON_SUPPRESS_CONFIG_ERRORS = None
+else:
+ if _envvar.lower() in {'1','true'}:
+ IPYTHON_SUPPRESS_CONFIG_ERRORS = True
+ elif _envvar.lower() in {'0','false'} :
+ IPYTHON_SUPPRESS_CONFIG_ERRORS = False
+ else:
+ sys.exit("Unsupported value for environment variable: 'IPYTHON_SUPPRESS_CONFIG_ERRORS' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar )
+
+# aliases and flags
+
+base_aliases = {}
+if isinstance(Application.aliases, dict):
+ # traitlets 5
+ base_aliases.update(Application.aliases)
+base_aliases.update(
+ {
+ "profile-dir": "ProfileDir.location",
+ "profile": "BaseIPythonApplication.profile",
+ "ipython-dir": "BaseIPythonApplication.ipython_dir",
+ "log-level": "Application.log_level",
+ "config": "BaseIPythonApplication.extra_config_file",
+ }
+)
+
+base_flags = dict()
+if isinstance(Application.flags, dict):
+ # traitlets 5
+ base_flags.update(Application.flags)
+base_flags.update(
+ dict(
+ debug=(
+ {"Application": {"log_level": logging.DEBUG}},
+ "set log level to logging.DEBUG (maximize logging output)",
+ ),
+ quiet=(
+ {"Application": {"log_level": logging.CRITICAL}},
+ "set log level to logging.CRITICAL (minimize logging output)",
+ ),
+ init=(
+ {
+ "BaseIPythonApplication": {
+ "copy_config_files": True,
+ "auto_create": True,
+ }
+ },
+ """Initialize profile with default config files. This is equivalent
+ to running `ipython profile create ` prior to startup.
+ """,
+ ),
+ )
+)
+
+
+class ProfileAwareConfigLoader(PyFileConfigLoader):
+ """A Python file config loader that is aware of IPython profiles."""
+ def load_subconfig(self, fname, path=None, profile=None):
+ if profile is not None:
+ try:
+ profile_dir = ProfileDir.find_profile_dir_by_name(
+ get_ipython_dir(),
+ profile,
+ )
+ except ProfileDirError:
+ return
+ path = profile_dir.location
+ return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path)
+
+class BaseIPythonApplication(Application):
+
+ name = u'ipython'
+ description = Unicode(u'IPython: an enhanced interactive Python shell.')
+ version = Unicode(release.version)
+
+ aliases = base_aliases
+ flags = base_flags
+ classes = List([ProfileDir])
+
+ # enable `load_subconfig('cfg.py', profile='name')`
+ python_config_loader_class = ProfileAwareConfigLoader
+
+ # Track whether the config_file has changed,
+ # because some logic happens only if we aren't using the default.
+ config_file_specified = Set()
+
+ config_file_name = Unicode()
+ @default('config_file_name')
+ def _config_file_name_default(self):
+ return self.name.replace('-','_') + u'_config.py'
+ @observe('config_file_name')
+ def _config_file_name_changed(self, change):
+ if change['new'] != change['old']:
+ self.config_file_specified.add(change['new'])
+
+ # The directory that contains IPython's builtin profiles.
+ builtin_profile_dir = Unicode(
+ os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
+ )
+
+ config_file_paths = List(Unicode())
+ @default('config_file_paths')
+ def _config_file_paths_default(self):
+ return []
+
+ extra_config_file = Unicode(
+ help="""Path to an extra config file to load.
+
+ If specified, load this config file in addition to any other IPython config.
+ """).tag(config=True)
+ @observe('extra_config_file')
+ def _extra_config_file_changed(self, change):
+ old = change['old']
+ new = change['new']
+ try:
+ self.config_files.remove(old)
+ except ValueError:
+ pass
+ self.config_file_specified.add(new)
+ self.config_files.append(new)
+
+ profile = Unicode(u'default',
+ help="""The IPython profile to use."""
+ ).tag(config=True)
+
+ @observe('profile')
+ def _profile_changed(self, change):
+ self.builtin_profile_dir = os.path.join(
+ get_ipython_package_dir(), u'config', u'profile', change['new']
+ )
+
+ add_ipython_dir_to_sys_path = Bool(
+ False,
+ """Should the IPython profile directory be added to sys path ?
+
+ This option was non-existing before IPython 8.0, and ipython_dir was added to
+ sys path to allow import of extensions present there. This was historical
+ baggage from when pip did not exist. This now default to false,
+ but can be set to true for legacy reasons.
+ """,
+ ).tag(config=True)
+
+ ipython_dir = Unicode(
+ help="""
+ The name of the IPython directory. This directory is used for logging
+ configuration (through profiles), history storage, etc. The default
+ is usually $HOME/.ipython. This option can also be specified through
+ the environment variable IPYTHONDIR.
+ """
+ ).tag(config=True)
+ @default('ipython_dir')
+ def _ipython_dir_default(self):
+ d = get_ipython_dir()
+ self._ipython_dir_changed({
+ 'name': 'ipython_dir',
+ 'old': d,
+ 'new': d,
+ })
+ return d
+
+ _in_init_profile_dir = False
+ profile_dir = Instance(ProfileDir, allow_none=True)
+ @default('profile_dir')
+ def _profile_dir_default(self):
+ # avoid recursion
+ if self._in_init_profile_dir:
+ return
+ # profile_dir requested early, force initialization
+ self.init_profile_dir()
+ return self.profile_dir
+
+ overwrite = Bool(False,
+ help="""Whether to overwrite existing config files when copying"""
+ ).tag(config=True)
+ auto_create = Bool(False,
+ help="""Whether to create profile dir if it doesn't exist"""
+ ).tag(config=True)
+
+ config_files = List(Unicode())
+ @default('config_files')
+ def _config_files_default(self):
+ return [self.config_file_name]
+
+ copy_config_files = Bool(False,
+ help="""Whether to install the default config files into the profile dir.
+ If a new profile is being created, and IPython contains config files for that
+ profile, then they will be staged into the new directory. Otherwise,
+ default config files will be automatically generated.
+ """).tag(config=True)
+
+ verbose_crash = Bool(False,
+ help="""Create a massive crash report when IPython encounters what may be an
+ internal error. The default is to append a short message to the
+ usual traceback""").tag(config=True)
+
+ # The class to use as the crash handler.
+ crash_handler_class = Type(crashhandler.CrashHandler)
+
+ @catch_config_error
+ def __init__(self, **kwargs):
+ super(BaseIPythonApplication, self).__init__(**kwargs)
+ # ensure current working directory exists
+ try:
+ os.getcwd()
+ except:
+ # exit if cwd doesn't exist
+ self.log.error("Current working directory doesn't exist.")
+ self.exit(1)
+
+ #-------------------------------------------------------------------------
+ # Various stages of Application creation
+ #-------------------------------------------------------------------------
+
+ def init_crash_handler(self):
+ """Create a crash handler, typically setting sys.excepthook to it."""
+ self.crash_handler = self.crash_handler_class(self)
+ sys.excepthook = self.excepthook
+ def unset_crashhandler():
+ sys.excepthook = sys.__excepthook__
+ atexit.register(unset_crashhandler)
+
+ def excepthook(self, etype, evalue, tb):
+ """this is sys.excepthook after init_crashhandler
+
+ set self.verbose_crash=True to use our full crashhandler, instead of
+ a regular traceback with a short message (crash_handler_lite)
+ """
+
+ if self.verbose_crash:
+ return self.crash_handler(etype, evalue, tb)
+ else:
+ return crashhandler.crash_handler_lite(etype, evalue, tb)
+
+ @observe('ipython_dir')
+ def _ipython_dir_changed(self, change):
+ old = change['old']
+ new = change['new']
+ if old is not Undefined:
+ str_old = os.path.abspath(old)
+ if str_old in sys.path:
+ sys.path.remove(str_old)
+ if self.add_ipython_dir_to_sys_path:
+ str_path = os.path.abspath(new)
+ sys.path.append(str_path)
+ ensure_dir_exists(new)
+ readme = os.path.join(new, "README")
+ readme_src = os.path.join(
+ get_ipython_package_dir(), "config", "profile", "README"
+ )
+ if not os.path.exists(readme) and os.path.exists(readme_src):
+ shutil.copy(readme_src, readme)
+ for d in ("extensions", "nbextensions"):
+ path = os.path.join(new, d)
+ try:
+ ensure_dir_exists(path)
+ except OSError as e:
+ # this will not be EEXIST
+ self.log.error("couldn't create path %s: %s", path, e)
+ self.log.debug("IPYTHONDIR set to: %s" % new)
+
+ def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
+ """Load the config file.
+
+ By default, errors in loading config are handled, and a warning
+ printed on screen. For testing, the suppress_errors option is set
+ to False, so errors will make tests fail.
+
+ `suppress_errors` default value is to be `None` in which case the
+ behavior default to the one of `traitlets.Application`.
+
+ The default value can be set :
+ - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
+ - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
+ - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
+
+ Any other value are invalid, and will make IPython exit with a non-zero return code.
+ """
+
+
+ self.log.debug("Searching path %s for config files", self.config_file_paths)
+ base_config = 'ipython_config.py'
+ self.log.debug("Attempting to load config file: %s" %
+ base_config)
+ try:
+ if suppress_errors is not None:
+ old_value = Application.raise_config_file_errors
+ Application.raise_config_file_errors = not suppress_errors;
+ Application.load_config_file(
+ self,
+ base_config,
+ path=self.config_file_paths
+ )
+ except ConfigFileNotFound:
+ # ignore errors loading parent
+ self.log.debug("Config file %s not found", base_config)
+ pass
+ if suppress_errors is not None:
+ Application.raise_config_file_errors = old_value
+
+ for config_file_name in self.config_files:
+ if not config_file_name or config_file_name == base_config:
+ continue
+ self.log.debug("Attempting to load config file: %s" %
+ self.config_file_name)
+ try:
+ Application.load_config_file(
+ self,
+ config_file_name,
+ path=self.config_file_paths
+ )
+ except ConfigFileNotFound:
+ # Only warn if the default config file was NOT being used.
+ if config_file_name in self.config_file_specified:
+ msg = self.log.warning
+ else:
+ msg = self.log.debug
+ msg("Config file not found, skipping: %s", config_file_name)
+ except Exception:
+ # For testing purposes.
+ if not suppress_errors:
+ raise
+ self.log.warning("Error loading config file: %s" %
+ self.config_file_name, exc_info=True)
+
+ def init_profile_dir(self):
+ """initialize the profile dir"""
+ self._in_init_profile_dir = True
+ if self.profile_dir is not None:
+ # already ran
+ return
+ if 'ProfileDir.location' not in self.config:
+ # location not specified, find by profile name
+ try:
+ p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
+ except ProfileDirError:
+ # not found, maybe create it (always create default profile)
+ if self.auto_create or self.profile == 'default':
+ try:
+ p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
+ except ProfileDirError:
+ self.log.fatal("Could not create profile: %r"%self.profile)
+ self.exit(1)
+ else:
+ self.log.info("Created profile dir: %r"%p.location)
+ else:
+ self.log.fatal("Profile %r not found."%self.profile)
+ self.exit(1)
+ else:
+ self.log.debug(f"Using existing profile dir: {p.location!r}")
+ else:
+ location = self.config.ProfileDir.location
+ # location is fully specified
+ try:
+ p = ProfileDir.find_profile_dir(location, self.config)
+ except ProfileDirError:
+ # not found, maybe create it
+ if self.auto_create:
+ try:
+ p = ProfileDir.create_profile_dir(location, self.config)
+ except ProfileDirError:
+ self.log.fatal("Could not create profile directory: %r"%location)
+ self.exit(1)
+ else:
+ self.log.debug("Creating new profile dir: %r"%location)
+ else:
+ self.log.fatal("Profile directory %r not found."%location)
+ self.exit(1)
+ else:
+ self.log.debug(f"Using existing profile dir: {p.location!r}")
+ # if profile_dir is specified explicitly, set profile name
+ dir_name = os.path.basename(p.location)
+ if dir_name.startswith('profile_'):
+ self.profile = dir_name[8:]
+
+ self.profile_dir = p
+ self.config_file_paths.append(p.location)
+ self._in_init_profile_dir = False
+
+ def init_config_files(self):
+ """[optionally] copy default config files into profile dir."""
+ self.config_file_paths.extend(ENV_CONFIG_DIRS)
+ self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
+ # copy config files
+ path = Path(self.builtin_profile_dir)
+ if self.copy_config_files:
+ src = self.profile
+
+ cfg = self.config_file_name
+ if path and (path / cfg).exists():
+ self.log.warning(
+ "Staging %r from %s into %r [overwrite=%s]"
+ % (cfg, src, self.profile_dir.location, self.overwrite)
+ )
+ self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
+ else:
+ self.stage_default_config_file()
+ else:
+ # Still stage *bundled* config files, but not generated ones
+ # This is necessary for `ipython profile=sympy` to load the profile
+ # on the first go
+ files = path.glob("*.py")
+ for fullpath in files:
+ cfg = fullpath.name
+ if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
+ # file was copied
+ self.log.warning("Staging bundled %s from %s into %r"%(
+ cfg, self.profile, self.profile_dir.location)
+ )
+
+
+ def stage_default_config_file(self):
+ """auto generate default config file, and stage it into the profile."""
+ s = self.generate_config_file()
+ config_file = Path(self.profile_dir.location) / self.config_file_name
+ if self.overwrite or not config_file.exists():
+ self.log.warning("Generating default config file: %r" % (config_file))
+ config_file.write_text(s, encoding="utf-8")
+
+ @catch_config_error
+ def initialize(self, argv=None):
+ # don't hook up crash handler before parsing command-line
+ self.parse_command_line(argv)
+ self.init_crash_handler()
+ if self.subapp is not None:
+ # stop here if subapp is taking over
+ return
+ # save a copy of CLI config to re-load after config files
+ # so that it has highest priority
+ cl_config = deepcopy(self.config)
+ self.init_profile_dir()
+ self.init_config_files()
+ self.load_config_file()
+ # enforce cl-opts override configfile opts:
+ self.update_config(cl_config)
diff --git a/venv/lib/python3.10/site-packages/IPython/core/async_helpers.py b/venv/lib/python3.10/site-packages/IPython/core/async_helpers.py
new file mode 100644
index 000000000..0e7db0bb5
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/core/async_helpers.py
@@ -0,0 +1,156 @@
+"""
+Async helper function that are invalid syntax on Python 3.5 and below.
+
+This code is best effort, and may have edge cases not behaving as expected. In
+particular it contain a number of heuristics to detect whether code is
+effectively async and need to run in an event loop or not.
+
+Some constructs (like top-level `return`, or `yield`) are taken care of
+explicitly to actually raise a SyntaxError and stay as close as possible to
+Python semantics.
+"""
+
+
+import ast
+import asyncio
+import inspect
+from functools import wraps
+
+_asyncio_event_loop = None
+
+
+def get_asyncio_loop():
+ """asyncio has deprecated get_event_loop
+
+ Replicate it here, with our desired semantics:
+
+ - always returns a valid, not-closed loop
+ - not thread-local like asyncio's,
+ because we only want one loop for IPython
+ - if called from inside a coroutine (e.g. in ipykernel),
+ return the running loop
+
+ .. versionadded:: 8.0
+ """
+ try:
+ return asyncio.get_running_loop()
+ except RuntimeError:
+ # not inside a coroutine,
+ # track our own global
+ pass
+
+ # not thread-local like asyncio's,
+ # because we only track one event loop to run for IPython itself,
+ # always in the main thread.
+ global _asyncio_event_loop
+ if _asyncio_event_loop is None or _asyncio_event_loop.is_closed():
+ _asyncio_event_loop = asyncio.new_event_loop()
+ return _asyncio_event_loop
+
+
+class _AsyncIORunner:
+ def __call__(self, coro):
+ """
+ Handler for asyncio autoawait
+ """
+ return get_asyncio_loop().run_until_complete(coro)
+
+ def __str__(self):
+ return "asyncio"
+
+
+_asyncio_runner = _AsyncIORunner()
+
+
+class _AsyncIOProxy:
+ """Proxy-object for an asyncio
+
+ Any coroutine methods will be wrapped in event_loop.run_
+ """
+
+ def __init__(self, obj, event_loop):
+ self._obj = obj
+ self._event_loop = event_loop
+
+ def __repr__(self):
+ return f"<_AsyncIOProxy({self._obj!r})>"
+
+ def __getattr__(self, key):
+ attr = getattr(self._obj, key)
+ if inspect.iscoroutinefunction(attr):
+ # if it's a coroutine method,
+ # return a threadsafe wrapper onto the _current_ asyncio loop
+ @wraps(attr)
+ def _wrapped(*args, **kwargs):
+ concurrent_future = asyncio.run_coroutine_threadsafe(
+ attr(*args, **kwargs), self._event_loop
+ )
+ return asyncio.wrap_future(concurrent_future)
+
+ return _wrapped
+ else:
+ return attr
+
+ def __dir__(self):
+ return dir(self._obj)
+
+
+def _curio_runner(coroutine):
+ """
+ handler for curio autoawait
+ """
+ import curio
+
+ return curio.run(coroutine)
+
+
+def _trio_runner(async_fn):
+ import trio
+
+ async def loc(coro):
+ """
+ We need the dummy no-op async def to protect from
+ trio's internal. See https://github.com/python-trio/trio/issues/89
+ """
+ return await coro
+
+ return trio.run(loc, async_fn)
+
+
+def _pseudo_sync_runner(coro):
+ """
+ A runner that does not really allow async execution, and just advance the coroutine.
+
+ See discussion in https://github.com/python-trio/trio/issues/608,
+
+ Credit to Nathaniel Smith
+ """
+ try:
+ coro.send(None)
+ except StopIteration as exc:
+ return exc.value
+ else:
+ # TODO: do not raise but return an execution result with the right info.
+ raise RuntimeError(
+ "{coro_name!r} needs a real async loop".format(coro_name=coro.__name__)
+ )
+
+
+def _should_be_async(cell: str) -> bool:
+ """Detect if a block of code need to be wrapped in an `async def`
+
+ Attempt to parse the block of code, it it compile we're fine.
+ Otherwise we wrap if and try to compile.
+
+ If it works, assume it should be async. Otherwise Return False.
+
+ Not handled yet: If the block of code has a return statement as the top
+ level, it will be seen as async. This is a know limitation.
+ """
+ try:
+ code = compile(
+ cell, "<>", "exec", flags=getattr(ast, "PyCF_ALLOW_TOP_LEVEL_AWAIT", 0x0)
+ )
+ return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
+ except (SyntaxError, MemoryError):
+ return False
diff --git a/venv/lib/python3.10/site-packages/IPython/core/autocall.py b/venv/lib/python3.10/site-packages/IPython/core/autocall.py
new file mode 100644
index 000000000..54beec3f5
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/core/autocall.py
@@ -0,0 +1,70 @@
+# encoding: utf-8
+"""
+Autocall capabilities for IPython.core.
+
+Authors:
+
+* Brian Granger
+* Fernando Perez
+* Thomas Kluyver
+
+Notes
+-----
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (C) 2008-2011 The IPython Development Team
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+
+
+#-----------------------------------------------------------------------------
+# Code
+#-----------------------------------------------------------------------------
+
+class IPyAutocall(object):
+ """ Instances of this class are always autocalled
+
+ This happens regardless of 'autocall' variable state. Use this to
+ develop macro-like mechanisms.
+ """
+ _ip = None
+ rewrite = True
+ def __init__(self, ip=None):
+ self._ip = ip
+
+ def set_ip(self, ip):
+ """Will be used to set _ip point to current ipython instance b/f call
+
+ Override this method if you don't want this to happen.
+
+ """
+ self._ip = ip
+
+
+class ExitAutocall(IPyAutocall):
+ """An autocallable object which will be added to the user namespace so that
+ exit, exit(), quit or quit() are all valid ways to close the shell."""
+ rewrite = False
+
+ def __call__(self):
+ self._ip.ask_exit()
+
+class ZMQExitAutocall(ExitAutocall):
+ """Exit IPython. Autocallable, so it needn't be explicitly called.
+
+ Parameters
+ ----------
+ keep_kernel : bool
+ If True, leave the kernel alive. Otherwise, tell the kernel to exit too
+ (default).
+ """
+ def __call__(self, keep_kernel=False):
+ self._ip.keepkernel_on_exit = keep_kernel
+ self._ip.ask_exit()
diff --git a/venv/lib/python3.10/site-packages/IPython/core/builtin_trap.py b/venv/lib/python3.10/site-packages/IPython/core/builtin_trap.py
new file mode 100644
index 000000000..a8ea4abcd
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/core/builtin_trap.py
@@ -0,0 +1,86 @@
+"""
+A context manager for managing things injected into :mod:`builtins`.
+"""
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+import builtins as builtin_mod
+
+from traitlets.config.configurable import Configurable
+
+from traitlets import Instance
+
+
+class __BuiltinUndefined(object): pass
+BuiltinUndefined = __BuiltinUndefined()
+
+class __HideBuiltin(object): pass
+HideBuiltin = __HideBuiltin()
+
+
+class BuiltinTrap(Configurable):
+
+ shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
+ allow_none=True)
+
+ def __init__(self, shell=None):
+ super(BuiltinTrap, self).__init__(shell=shell, config=None)
+ self._orig_builtins = {}
+ # We define this to track if a single BuiltinTrap is nested.
+ # Only turn off the trap when the outermost call to __exit__ is made.
+ self._nested_level = 0
+ self.shell = shell
+ # builtins we always add - if set to HideBuiltin, they will just
+ # be removed instead of being replaced by something else
+ self.auto_builtins = {'exit': HideBuiltin,
+ 'quit': HideBuiltin,
+ 'get_ipython': self.shell.get_ipython,
+ }
+
+ def __enter__(self):
+ if self._nested_level == 0:
+ self.activate()
+ self._nested_level += 1
+ # I return self, so callers can use add_builtin in a with clause.
+ return self
+
+ def __exit__(self, type, value, traceback):
+ if self._nested_level == 1:
+ self.deactivate()
+ self._nested_level -= 1
+ # Returning False will cause exceptions to propagate
+ return False
+
+ def add_builtin(self, key, value):
+ """Add a builtin and save the original."""
+ bdict = builtin_mod.__dict__
+ orig = bdict.get(key, BuiltinUndefined)
+ if value is HideBuiltin:
+ if orig is not BuiltinUndefined: #same as 'key in bdict'
+ self._orig_builtins[key] = orig
+ del bdict[key]
+ else:
+ self._orig_builtins[key] = orig
+ bdict[key] = value
+
+ def remove_builtin(self, key, orig):
+ """Remove an added builtin and re-set the original."""
+ if orig is BuiltinUndefined:
+ del builtin_mod.__dict__[key]
+ else:
+ builtin_mod.__dict__[key] = orig
+
+ def activate(self):
+ """Store ipython references in the __builtin__ namespace."""
+
+ add_builtin = self.add_builtin
+ for name, func in self.auto_builtins.items():
+ add_builtin(name, func)
+
+ def deactivate(self):
+ """Remove any builtins which might have been added by add_builtins, or
+ restore overwritten ones to their previous values."""
+ remove_builtin = self.remove_builtin
+ for key, val in self._orig_builtins.items():
+ remove_builtin(key, val)
+ self._orig_builtins.clear()
+ self._builtins_added = False
diff --git a/venv/lib/python3.10/site-packages/IPython/core/compilerop.py b/venv/lib/python3.10/site-packages/IPython/core/compilerop.py
new file mode 100644
index 000000000..7799a4fc9
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/core/compilerop.py
@@ -0,0 +1,214 @@
+"""Compiler tools with improved interactive support.
+
+Provides compilation machinery similar to codeop, but with caching support so
+we can provide interactive tracebacks.
+
+Authors
+-------
+* Robert Kern
+* Fernando Perez
+* Thomas Kluyver
+"""
+
+# Note: though it might be more natural to name this module 'compiler', that
+# name is in the stdlib and name collisions with the stdlib tend to produce
+# weird problems (often with third-party tools).
+
+#-----------------------------------------------------------------------------
+# Copyright (C) 2010-2011 The IPython Development Team.
+#
+# Distributed under the terms of the BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+
+# Stdlib imports
+import __future__
+from ast import PyCF_ONLY_AST
+import codeop
+import functools
+import hashlib
+import linecache
+import operator
+import time
+from contextlib import contextmanager
+
+#-----------------------------------------------------------------------------
+# Constants
+#-----------------------------------------------------------------------------
+
+# Roughly equal to PyCF_MASK | PyCF_MASK_OBSOLETE as defined in pythonrun.h,
+# this is used as a bitmask to extract future-related code flags.
+PyCF_MASK = functools.reduce(operator.or_,
+ (getattr(__future__, fname).compiler_flag
+ for fname in __future__.all_feature_names))
+
+#-----------------------------------------------------------------------------
+# Local utilities
+#-----------------------------------------------------------------------------
+
+def code_name(code, number=0):
+ """ Compute a (probably) unique name for code for caching.
+
+ This now expects code to be unicode.
+ """
+ hash_digest = hashlib.sha1(code.encode("utf-8")).hexdigest()
+ # Include the number and 12 characters of the hash in the name. It's
+ # pretty much impossible that in a single session we'll have collisions
+ # even with truncated hashes, and the full one makes tracebacks too long
+ return ''.format(number, hash_digest[:12])
+
+#-----------------------------------------------------------------------------
+# Classes and functions
+#-----------------------------------------------------------------------------
+
+class CachingCompiler(codeop.Compile):
+ """A compiler that caches code compiled from interactive statements.
+ """
+
+ def __init__(self):
+ codeop.Compile.__init__(self)
+
+ # Caching a dictionary { filename: execution_count } for nicely
+ # rendered tracebacks. The filename corresponds to the filename
+ # argument used for the builtins.compile function.
+ self._filename_map = {}
+
+ def ast_parse(self, source, filename='', symbol='exec'):
+ """Parse code to an AST with the current compiler flags active.
+
+ Arguments are exactly the same as ast.parse (in the standard library),
+ and are passed to the built-in compile function."""
+ return compile(source, filename, symbol, self.flags | PyCF_ONLY_AST, 1)
+
+ def reset_compiler_flags(self):
+ """Reset compiler flags to default state."""
+ # This value is copied from codeop.Compile.__init__, so if that ever
+ # changes, it will need to be updated.
+ self.flags = codeop.PyCF_DONT_IMPLY_DEDENT
+
+ @property
+ def compiler_flags(self):
+ """Flags currently active in the compilation process.
+ """
+ return self.flags
+
+ def get_code_name(self, raw_code, transformed_code, number):
+ """Compute filename given the code, and the cell number.
+
+ Parameters
+ ----------
+ raw_code : str
+ The raw cell code.
+ transformed_code : str
+ The executable Python source code to cache and compile.
+ number : int
+ A number which forms part of the code's name. Used for the execution
+ counter.
+
+ Returns
+ -------
+ The computed filename.
+ """
+ return code_name(transformed_code, number)
+
+ def format_code_name(self, name):
+ """Return a user-friendly label and name for a code block.
+
+ Parameters
+ ----------
+ name : str
+ The name for the code block returned from get_code_name
+
+ Returns
+ -------
+ A (label, name) pair that can be used in tracebacks, or None if the default formatting should be used.
+ """
+ if name in self._filename_map:
+ return "Cell", "In[%s]" % self._filename_map[name]
+
+ def cache(self, transformed_code, number=0, raw_code=None):
+ """Make a name for a block of code, and cache the code.
+
+ Parameters
+ ----------
+ transformed_code : str
+ The executable Python source code to cache and compile.
+ number : int
+ A number which forms part of the code's name. Used for the execution
+ counter.
+ raw_code : str
+ The raw code before transformation, if None, set to `transformed_code`.
+
+ Returns
+ -------
+ The name of the cached code (as a string). Pass this as the filename
+ argument to compilation, so that tracebacks are correctly hooked up.
+ """
+ if raw_code is None:
+ raw_code = transformed_code
+
+ name = self.get_code_name(raw_code, transformed_code, number)
+
+ # Save the execution count
+ self._filename_map[name] = number
+
+ # Since Python 2.5, setting mtime to `None` means the lines will
+ # never be removed by `linecache.checkcache`. This means all the
+ # monkeypatching has *never* been necessary, since this code was
+ # only added in 2010, at which point IPython had already stopped
+ # supporting Python 2.4.
+ #
+ # Note that `linecache.clearcache` and `linecache.updatecache` may
+ # still remove our code from the cache, but those show explicit
+ # intent, and we should not try to interfere. Normally the former
+ # is never called except when out of memory, and the latter is only
+ # called for lines *not* in the cache.
+ entry = (
+ len(transformed_code),
+ None,
+ [line + "\n" for line in transformed_code.splitlines()],
+ name,
+ )
+ linecache.cache[name] = entry
+ return name
+
+ @contextmanager
+ def extra_flags(self, flags):
+ ## bits that we'll set to 1
+ turn_on_bits = ~self.flags & flags
+
+
+ self.flags = self.flags | flags
+ try:
+ yield
+ finally:
+ # turn off only the bits we turned on so that something like
+ # __future__ that set flags stays.
+ self.flags &= ~turn_on_bits
+
+
+def check_linecache_ipython(*args):
+ """Deprecated since IPython 8.6. Call linecache.checkcache() directly.
+
+ It was already not necessary to call this function directly. If no
+ CachingCompiler had been created, this function would fail badly. If
+ an instance had been created, this function would've been monkeypatched
+ into place.
+
+ As of IPython 8.6, the monkeypatching has gone away entirely. But there
+ were still internal callers of this function, so maybe external callers
+ also existed?
+ """
+ import warnings
+
+ warnings.warn(
+ "Deprecated Since IPython 8.6, Just call linecache.checkcache() directly.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ linecache.checkcache()
diff --git a/venv/lib/python3.10/site-packages/IPython/core/completer.py b/venv/lib/python3.10/site-packages/IPython/core/completer.py
new file mode 100644
index 000000000..f0bbb4e56
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/core/completer.py
@@ -0,0 +1,3322 @@
+"""Completion for IPython.
+
+This module started as fork of the rlcompleter module in the Python standard
+library. The original enhancements made to rlcompleter have been sent
+upstream and were accepted as of Python 2.3,
+
+This module now support a wide variety of completion mechanism both available
+for normal classic Python code, as well as completer for IPython specific
+Syntax like magics.
+
+Latex and Unicode completion
+============================
+
+IPython and compatible frontends not only can complete your code, but can help
+you to input a wide range of characters. In particular we allow you to insert
+a unicode character using the tab completion mechanism.
+
+Forward latex/unicode completion
+--------------------------------
+
+Forward completion allows you to easily type a unicode character using its latex
+name, or unicode long description. To do so type a backslash follow by the
+relevant name and press tab:
+
+
+Using latex completion:
+
+.. code::
+
+ \\alpha
+ α
+
+or using unicode completion:
+
+
+.. code::
+
+ \\GREEK SMALL LETTER ALPHA
+ α
+
+
+Only valid Python identifiers will complete. Combining characters (like arrow or
+dots) are also available, unlike latex they need to be put after the their
+counterpart that is to say, ``F\\\\vec`` is correct, not ``\\\\vecF``.
+
+Some browsers are known to display combining characters incorrectly.
+
+Backward latex completion
+-------------------------
+
+It is sometime challenging to know how to type a character, if you are using
+IPython, or any compatible frontend you can prepend backslash to the character
+and press :kbd:`Tab` to expand it to its latex form.
+
+.. code::
+
+ \\α
+ \\alpha
+
+
+Both forward and backward completions can be deactivated by setting the
+:std:configtrait:`Completer.backslash_combining_completions` option to
+``False``.
+
+
+Experimental
+============
+
+Starting with IPython 6.0, this module can make use of the Jedi library to
+generate completions both using static analysis of the code, and dynamically
+inspecting multiple namespaces. Jedi is an autocompletion and static analysis
+for Python. The APIs attached to this new mechanism is unstable and will
+raise unless use in an :any:`provisionalcompleter` context manager.
+
+You will find that the following are experimental:
+
+ - :any:`provisionalcompleter`
+ - :any:`IPCompleter.completions`
+ - :any:`Completion`
+ - :any:`rectify_completions`
+
+.. note::
+
+ better name for :any:`rectify_completions` ?
+
+We welcome any feedback on these new API, and we also encourage you to try this
+module in debug mode (start IPython with ``--Completer.debug=True``) in order
+to have extra logging information if :any:`jedi` is crashing, or if current
+IPython completer pending deprecations are returning results not yet handled
+by :any:`jedi`
+
+Using Jedi for tab completion allow snippets like the following to work without
+having to execute any code:
+
+ >>> myvar = ['hello', 42]
+ ... myvar[1].bi
+
+Tab completion will be able to infer that ``myvar[1]`` is a real number without
+executing almost any code unlike the deprecated :any:`IPCompleter.greedy`
+option.
+
+Be sure to update :any:`jedi` to the latest stable version or to try the
+current development version to get better completions.
+
+Matchers
+========
+
+All completions routines are implemented using unified *Matchers* API.
+The matchers API is provisional and subject to change without notice.
+
+The built-in matchers include:
+
+- :any:`IPCompleter.dict_key_matcher`: dictionary key completions,
+- :any:`IPCompleter.magic_matcher`: completions for magics,
+- :any:`IPCompleter.unicode_name_matcher`,
+ :any:`IPCompleter.fwd_unicode_matcher`
+ and :any:`IPCompleter.latex_name_matcher`: see `Forward latex/unicode completion`_,
+- :any:`back_unicode_name_matcher` and :any:`back_latex_name_matcher`: see `Backward latex completion`_,
+- :any:`IPCompleter.file_matcher`: paths to files and directories,
+- :any:`IPCompleter.python_func_kw_matcher` - function keywords,
+- :any:`IPCompleter.python_matches` - globals and attributes (v1 API),
+- ``IPCompleter.jedi_matcher`` - static analysis with Jedi,
+- :any:`IPCompleter.custom_completer_matcher` - pluggable completer with a default
+ implementation in :any:`InteractiveShell` which uses IPython hooks system
+ (`complete_command`) with string dispatch (including regular expressions).
+ Differently to other matchers, ``custom_completer_matcher`` will not suppress
+ Jedi results to match behaviour in earlier IPython versions.
+
+Custom matchers can be added by appending to ``IPCompleter.custom_matchers`` list.
+
+Matcher API
+-----------
+
+Simplifying some details, the ``Matcher`` interface can described as
+
+.. code-block::
+
+ MatcherAPIv1 = Callable[[str], list[str]]
+ MatcherAPIv2 = Callable[[CompletionContext], SimpleMatcherResult]
+
+ Matcher = MatcherAPIv1 | MatcherAPIv2
+
+The ``MatcherAPIv1`` reflects the matcher API as available prior to IPython 8.6.0
+and remains supported as a simplest way for generating completions. This is also
+currently the only API supported by the IPython hooks system `complete_command`.
+
+To distinguish between matcher versions ``matcher_api_version`` attribute is used.
+More precisely, the API allows to omit ``matcher_api_version`` for v1 Matchers,
+and requires a literal ``2`` for v2 Matchers.
+
+Once the API stabilises future versions may relax the requirement for specifying
+``matcher_api_version`` by switching to :any:`functools.singledispatch`, therefore
+please do not rely on the presence of ``matcher_api_version`` for any purposes.
+
+Suppression of competing matchers
+---------------------------------
+
+By default results from all matchers are combined, in the order determined by
+their priority. Matchers can request to suppress results from subsequent
+matchers by setting ``suppress`` to ``True`` in the ``MatcherResult``.
+
+When multiple matchers simultaneously request surpression, the results from of
+the matcher with higher priority will be returned.
+
+Sometimes it is desirable to suppress most but not all other matchers;
+this can be achieved by adding a list of identifiers of matchers which
+should not be suppressed to ``MatcherResult`` under ``do_not_suppress`` key.
+
+The suppression behaviour can is user-configurable via
+:std:configtrait:`IPCompleter.suppress_competing_matchers`.
+"""
+
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+#
+# Some of this code originated from rlcompleter in the Python standard library
+# Copyright (C) 2001 Python Software Foundation, www.python.org
+
+from __future__ import annotations
+import builtins as builtin_mod
+import enum
+import glob
+import inspect
+import itertools
+import keyword
+import os
+import re
+import string
+import sys
+import tokenize
+import time
+import unicodedata
+import uuid
+import warnings
+from ast import literal_eval
+from collections import defaultdict
+from contextlib import contextmanager
+from dataclasses import dataclass
+from functools import cached_property, partial
+from types import SimpleNamespace
+from typing import (
+ Iterable,
+ Iterator,
+ List,
+ Tuple,
+ Union,
+ Any,
+ Sequence,
+ Dict,
+ Optional,
+ TYPE_CHECKING,
+ Set,
+ Sized,
+ TypeVar,
+ Literal,
+)
+
+from IPython.core.guarded_eval import guarded_eval, EvaluationContext
+from IPython.core.error import TryNext
+from IPython.core.inputtransformer2 import ESC_MAGIC
+from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol
+from IPython.core.oinspect import InspectColors
+from IPython.testing.skipdoctest import skip_doctest
+from IPython.utils import generics
+from IPython.utils.decorators import sphinx_options
+from IPython.utils.dir2 import dir2, get_real_method
+from IPython.utils.docs import GENERATING_DOCUMENTATION
+from IPython.utils.path import ensure_dir_exists
+from IPython.utils.process import arg_split
+from traitlets import (
+ Bool,
+ Enum,
+ Int,
+ List as ListTrait,
+ Unicode,
+ Dict as DictTrait,
+ Union as UnionTrait,
+ observe,
+)
+from traitlets.config.configurable import Configurable
+
+import __main__
+
+# skip module docstests
+__skip_doctest__ = True
+
+
+try:
+ import jedi
+ jedi.settings.case_insensitive_completion = False
+ import jedi.api.helpers
+ import jedi.api.classes
+ JEDI_INSTALLED = True
+except ImportError:
+ JEDI_INSTALLED = False
+
+
+if TYPE_CHECKING or GENERATING_DOCUMENTATION and sys.version_info >= (3, 11):
+ from typing import cast
+ from typing_extensions import TypedDict, NotRequired, Protocol, TypeAlias, TypeGuard
+else:
+ from typing import Generic
+
+ def cast(type_, obj):
+ """Workaround for `TypeError: MatcherAPIv2() takes no arguments`"""
+ return obj
+
+ # do not require on runtime
+ NotRequired = Tuple # requires Python >=3.11
+ TypedDict = Dict # by extension of `NotRequired` requires 3.11 too
+ Protocol = object # requires Python >=3.8
+ TypeAlias = Any # requires Python >=3.10
+ TypeGuard = Generic # requires Python >=3.10
+if GENERATING_DOCUMENTATION:
+ from typing import TypedDict
+
+# -----------------------------------------------------------------------------
+# Globals
+#-----------------------------------------------------------------------------
+
+# ranges where we have most of the valid unicode names. We could be more finer
+# grained but is it worth it for performance While unicode have character in the
+# range 0, 0x110000, we seem to have name for about 10% of those. (131808 as I
+# write this). With below range we cover them all, with a density of ~67%
+# biggest next gap we consider only adds up about 1% density and there are 600
+# gaps that would need hard coding.
+_UNICODE_RANGES = [(32, 0x323B0), (0xE0001, 0xE01F0)]
+
+# Public API
+__all__ = ["Completer", "IPCompleter"]
+
+if sys.platform == 'win32':
+ PROTECTABLES = ' '
+else:
+ PROTECTABLES = ' ()[]{}?=\\|;:\'#*"^&'
+
+# Protect against returning an enormous number of completions which the frontend
+# may have trouble processing.
+MATCHES_LIMIT = 500
+
+# Completion type reported when no type can be inferred.
+_UNKNOWN_TYPE = ""
+
+# sentinel value to signal lack of a match
+not_found = object()
+
+class ProvisionalCompleterWarning(FutureWarning):
+ """
+ Exception raise by an experimental feature in this module.
+
+ Wrap code in :any:`provisionalcompleter` context manager if you
+ are certain you want to use an unstable feature.
+ """
+ pass
+
+warnings.filterwarnings('error', category=ProvisionalCompleterWarning)
+
+
+@skip_doctest
+@contextmanager
+def provisionalcompleter(action='ignore'):
+ """
+ This context manager has to be used in any place where unstable completer
+ behavior and API may be called.
+
+ >>> with provisionalcompleter():
+ ... completer.do_experimental_things() # works
+
+ >>> completer.do_experimental_things() # raises.
+
+ .. note::
+
+ Unstable
+
+ By using this context manager you agree that the API in use may change
+ without warning, and that you won't complain if they do so.
+
+ You also understand that, if the API is not to your liking, you should report
+ a bug to explain your use case upstream.
+
+ We'll be happy to get your feedback, feature requests, and improvements on
+ any of the unstable APIs!
+ """
+ with warnings.catch_warnings():
+ warnings.filterwarnings(action, category=ProvisionalCompleterWarning)
+ yield
+
+
+def has_open_quotes(s):
+ """Return whether a string has open quotes.
+
+ This simply counts whether the number of quote characters of either type in
+ the string is odd.
+
+ Returns
+ -------
+ If there is an open quote, the quote character is returned. Else, return
+ False.
+ """
+ # We check " first, then ', so complex cases with nested quotes will get
+ # the " to take precedence.
+ if s.count('"') % 2:
+ return '"'
+ elif s.count("'") % 2:
+ return "'"
+ else:
+ return False
+
+
+def protect_filename(s, protectables=PROTECTABLES):
+ """Escape a string to protect certain characters."""
+ if set(s) & set(protectables):
+ if sys.platform == "win32":
+ return '"' + s + '"'
+ else:
+ return "".join(("\\" + c if c in protectables else c) for c in s)
+ else:
+ return s
+
+
+def expand_user(path:str) -> Tuple[str, bool, str]:
+ """Expand ``~``-style usernames in strings.
+
+ This is similar to :func:`os.path.expanduser`, but it computes and returns
+ extra information that will be useful if the input was being used in
+ computing completions, and you wish to return the completions with the
+ original '~' instead of its expanded value.
+
+ Parameters
+ ----------
+ path : str
+ String to be expanded. If no ~ is present, the output is the same as the
+ input.
+
+ Returns
+ -------
+ newpath : str
+ Result of ~ expansion in the input path.
+ tilde_expand : bool
+ Whether any expansion was performed or not.
+ tilde_val : str
+ The value that ~ was replaced with.
+ """
+ # Default values
+ tilde_expand = False
+ tilde_val = ''
+ newpath = path
+
+ if path.startswith('~'):
+ tilde_expand = True
+ rest = len(path)-1
+ newpath = os.path.expanduser(path)
+ if rest:
+ tilde_val = newpath[:-rest]
+ else:
+ tilde_val = newpath
+
+ return newpath, tilde_expand, tilde_val
+
+
+def compress_user(path:str, tilde_expand:bool, tilde_val:str) -> str:
+ """Does the opposite of expand_user, with its outputs.
+ """
+ if tilde_expand:
+ return path.replace(tilde_val, '~')
+ else:
+ return path
+
+
+def completions_sorting_key(word):
+ """key for sorting completions
+
+ This does several things:
+
+ - Demote any completions starting with underscores to the end
+ - Insert any %magic and %%cellmagic completions in the alphabetical order
+ by their name
+ """
+ prio1, prio2 = 0, 0
+
+ if word.startswith('__'):
+ prio1 = 2
+ elif word.startswith('_'):
+ prio1 = 1
+
+ if word.endswith('='):
+ prio1 = -1
+
+ if word.startswith('%%'):
+ # If there's another % in there, this is something else, so leave it alone
+ if not "%" in word[2:]:
+ word = word[2:]
+ prio2 = 2
+ elif word.startswith('%'):
+ if not "%" in word[1:]:
+ word = word[1:]
+ prio2 = 1
+
+ return prio1, word, prio2
+
+
+class _FakeJediCompletion:
+ """
+ This is a workaround to communicate to the UI that Jedi has crashed and to
+ report a bug. Will be used only id :any:`IPCompleter.debug` is set to true.
+
+ Added in IPython 6.0 so should likely be removed for 7.0
+
+ """
+
+ def __init__(self, name):
+
+ self.name = name
+ self.complete = name
+ self.type = 'crashed'
+ self.name_with_symbols = name
+ self.signature = ""
+ self._origin = "fake"
+ self.text = "crashed"
+
+ def __repr__(self):
+ return ''
+
+
+_JediCompletionLike = Union[jedi.api.Completion, _FakeJediCompletion]
+
+
+class Completion:
+ """
+ Completion object used and returned by IPython completers.
+
+ .. warning::
+
+ Unstable
+
+ This function is unstable, API may change without warning.
+ It will also raise unless use in proper context manager.
+
+ This act as a middle ground :any:`Completion` object between the
+ :any:`jedi.api.classes.Completion` object and the Prompt Toolkit completion
+ object. While Jedi need a lot of information about evaluator and how the
+ code should be ran/inspected, PromptToolkit (and other frontend) mostly
+ need user facing information.
+
+ - Which range should be replaced replaced by what.
+ - Some metadata (like completion type), or meta information to displayed to
+ the use user.
+
+ For debugging purpose we can also store the origin of the completion (``jedi``,
+ ``IPython.python_matches``, ``IPython.magics_matches``...).
+ """
+
+ __slots__ = ['start', 'end', 'text', 'type', 'signature', '_origin']
+
+ def __init__(
+ self,
+ start: int,
+ end: int,
+ text: str,
+ *,
+ type: Optional[str] = None,
+ _origin="",
+ signature="",
+ ) -> None:
+ warnings.warn(
+ "``Completion`` is a provisional API (as of IPython 6.0). "
+ "It may change without warnings. "
+ "Use in corresponding context manager.",
+ category=ProvisionalCompleterWarning,
+ stacklevel=2,
+ )
+
+ self.start = start
+ self.end = end
+ self.text = text
+ self.type = type
+ self.signature = signature
+ self._origin = _origin
+
+ def __repr__(self):
+ return '' % \
+ (self.start, self.end, self.text, self.type or '?', self.signature or '?')
+
+ def __eq__(self, other) -> bool:
+ """
+ Equality and hash do not hash the type (as some completer may not be
+ able to infer the type), but are use to (partially) de-duplicate
+ completion.
+
+ Completely de-duplicating completion is a bit tricker that just
+ comparing as it depends on surrounding text, which Completions are not
+ aware of.
+ """
+ return self.start == other.start and \
+ self.end == other.end and \
+ self.text == other.text
+
+ def __hash__(self):
+ return hash((self.start, self.end, self.text))
+
+
+class SimpleCompletion:
+ """Completion item to be included in the dictionary returned by new-style Matcher (API v2).
+
+ .. warning::
+
+ Provisional
+
+ This class is used to describe the currently supported attributes of
+ simple completion items, and any additional implementation details
+ should not be relied on. Additional attributes may be included in
+ future versions, and meaning of text disambiguated from the current
+ dual meaning of "text to insert" and "text to used as a label".
+ """
+
+ __slots__ = ["text", "type"]
+
+ def __init__(self, text: str, *, type: Optional[str] = None):
+ self.text = text
+ self.type = type
+
+ def __repr__(self):
+ return f""
+
+
+class _MatcherResultBase(TypedDict):
+ """Definition of dictionary to be returned by new-style Matcher (API v2)."""
+
+ #: Suffix of the provided ``CompletionContext.token``, if not given defaults to full token.
+ matched_fragment: NotRequired[str]
+
+ #: Whether to suppress results from all other matchers (True), some
+ #: matchers (set of identifiers) or none (False); default is False.
+ suppress: NotRequired[Union[bool, Set[str]]]
+
+ #: Identifiers of matchers which should NOT be suppressed when this matcher
+ #: requests to suppress all other matchers; defaults to an empty set.
+ do_not_suppress: NotRequired[Set[str]]
+
+ #: Are completions already ordered and should be left as-is? default is False.
+ ordered: NotRequired[bool]
+
+
+@sphinx_options(show_inherited_members=True, exclude_inherited_from=["dict"])
+class SimpleMatcherResult(_MatcherResultBase, TypedDict):
+ """Result of new-style completion matcher."""
+
+ # note: TypedDict is added again to the inheritance chain
+ # in order to get __orig_bases__ for documentation
+
+ #: List of candidate completions
+ completions: Sequence[SimpleCompletion] | Iterator[SimpleCompletion]
+
+
+class _JediMatcherResult(_MatcherResultBase):
+ """Matching result returned by Jedi (will be processed differently)"""
+
+ #: list of candidate completions
+ completions: Iterator[_JediCompletionLike]
+
+
+AnyMatcherCompletion = Union[_JediCompletionLike, SimpleCompletion]
+AnyCompletion = TypeVar("AnyCompletion", AnyMatcherCompletion, Completion)
+
+
+@dataclass
+class CompletionContext:
+ """Completion context provided as an argument to matchers in the Matcher API v2."""
+
+ # rationale: many legacy matchers relied on completer state (`self.text_until_cursor`)
+ # which was not explicitly visible as an argument of the matcher, making any refactor
+ # prone to errors; by explicitly passing `cursor_position` we can decouple the matchers
+ # from the completer, and make substituting them in sub-classes easier.
+
+ #: Relevant fragment of code directly preceding the cursor.
+ #: The extraction of token is implemented via splitter heuristic
+ #: (following readline behaviour for legacy reasons), which is user configurable
+ #: (by switching the greedy mode).
+ token: str
+
+ #: The full available content of the editor or buffer
+ full_text: str
+
+ #: Cursor position in the line (the same for ``full_text`` and ``text``).
+ cursor_position: int
+
+ #: Cursor line in ``full_text``.
+ cursor_line: int
+
+ #: The maximum number of completions that will be used downstream.
+ #: Matchers can use this information to abort early.
+ #: The built-in Jedi matcher is currently excepted from this limit.
+ # If not given, return all possible completions.
+ limit: Optional[int]
+
+ @cached_property
+ def text_until_cursor(self) -> str:
+ return self.line_with_cursor[: self.cursor_position]
+
+ @cached_property
+ def line_with_cursor(self) -> str:
+ return self.full_text.split("\n")[self.cursor_line]
+
+
+#: Matcher results for API v2.
+MatcherResult = Union[SimpleMatcherResult, _JediMatcherResult]
+
+
+class _MatcherAPIv1Base(Protocol):
+ def __call__(self, text: str) -> List[str]:
+ """Call signature."""
+ ...
+
+ #: Used to construct the default matcher identifier
+ __qualname__: str
+
+
+class _MatcherAPIv1Total(_MatcherAPIv1Base, Protocol):
+ #: API version
+ matcher_api_version: Optional[Literal[1]]
+
+ def __call__(self, text: str) -> List[str]:
+ """Call signature."""
+ ...
+
+
+#: Protocol describing Matcher API v1.
+MatcherAPIv1: TypeAlias = Union[_MatcherAPIv1Base, _MatcherAPIv1Total]
+
+
+class MatcherAPIv2(Protocol):
+ """Protocol describing Matcher API v2."""
+
+ #: API version
+ matcher_api_version: Literal[2] = 2
+
+ def __call__(self, context: CompletionContext) -> MatcherResult:
+ """Call signature."""
+ ...
+
+ #: Used to construct the default matcher identifier
+ __qualname__: str
+
+
+Matcher: TypeAlias = Union[MatcherAPIv1, MatcherAPIv2]
+
+
+def _is_matcher_v1(matcher: Matcher) -> TypeGuard[MatcherAPIv1]:
+ api_version = _get_matcher_api_version(matcher)
+ return api_version == 1
+
+
+def _is_matcher_v2(matcher: Matcher) -> TypeGuard[MatcherAPIv2]:
+ api_version = _get_matcher_api_version(matcher)
+ return api_version == 2
+
+
+def _is_sizable(value: Any) -> TypeGuard[Sized]:
+ """Determines whether objects is sizable"""
+ return hasattr(value, "__len__")
+
+
+def _is_iterator(value: Any) -> TypeGuard[Iterator]:
+ """Determines whether objects is sizable"""
+ return hasattr(value, "__next__")
+
+
+def has_any_completions(result: MatcherResult) -> bool:
+ """Check if any result includes any completions."""
+ completions = result["completions"]
+ if _is_sizable(completions):
+ return len(completions) != 0
+ if _is_iterator(completions):
+ try:
+ old_iterator = completions
+ first = next(old_iterator)
+ result["completions"] = cast(
+ Iterator[SimpleCompletion],
+ itertools.chain([first], old_iterator),
+ )
+ return True
+ except StopIteration:
+ return False
+ raise ValueError(
+ "Completions returned by matcher need to be an Iterator or a Sizable"
+ )
+
+
+def completion_matcher(
+ *,
+ priority: Optional[float] = None,
+ identifier: Optional[str] = None,
+ api_version: int = 1,
+):
+ """Adds attributes describing the matcher.
+
+ Parameters
+ ----------
+ priority : Optional[float]
+ The priority of the matcher, determines the order of execution of matchers.
+ Higher priority means that the matcher will be executed first. Defaults to 0.
+ identifier : Optional[str]
+ identifier of the matcher allowing users to modify the behaviour via traitlets,
+ and also used to for debugging (will be passed as ``origin`` with the completions).
+
+ Defaults to matcher function's ``__qualname__`` (for example,
+ ``IPCompleter.file_matcher`` for the built-in matched defined
+ as a ``file_matcher`` method of the ``IPCompleter`` class).
+ api_version: Optional[int]
+ version of the Matcher API used by this matcher.
+ Currently supported values are 1 and 2.
+ Defaults to 1.
+ """
+
+ def wrapper(func: Matcher):
+ func.matcher_priority = priority or 0 # type: ignore
+ func.matcher_identifier = identifier or func.__qualname__ # type: ignore
+ func.matcher_api_version = api_version # type: ignore
+ if TYPE_CHECKING:
+ if api_version == 1:
+ func = cast(MatcherAPIv1, func)
+ elif api_version == 2:
+ func = cast(MatcherAPIv2, func)
+ return func
+
+ return wrapper
+
+
+def _get_matcher_priority(matcher: Matcher):
+ return getattr(matcher, "matcher_priority", 0)
+
+
+def _get_matcher_id(matcher: Matcher):
+ return getattr(matcher, "matcher_identifier", matcher.__qualname__)
+
+
+def _get_matcher_api_version(matcher):
+ return getattr(matcher, "matcher_api_version", 1)
+
+
+context_matcher = partial(completion_matcher, api_version=2)
+
+
+_IC = Iterable[Completion]
+
+
+def _deduplicate_completions(text: str, completions: _IC)-> _IC:
+ """
+ Deduplicate a set of completions.
+
+ .. warning::
+
+ Unstable
+
+ This function is unstable, API may change without warning.
+
+ Parameters
+ ----------
+ text : str
+ text that should be completed.
+ completions : Iterator[Completion]
+ iterator over the completions to deduplicate
+
+ Yields
+ ------
+ `Completions` objects
+ Completions coming from multiple sources, may be different but end up having
+ the same effect when applied to ``text``. If this is the case, this will
+ consider completions as equal and only emit the first encountered.
+ Not folded in `completions()` yet for debugging purpose, and to detect when
+ the IPython completer does return things that Jedi does not, but should be
+ at some point.
+ """
+ completions = list(completions)
+ if not completions:
+ return
+
+ new_start = min(c.start for c in completions)
+ new_end = max(c.end for c in completions)
+
+ seen = set()
+ for c in completions:
+ new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
+ if new_text not in seen:
+ yield c
+ seen.add(new_text)
+
+
+def rectify_completions(text: str, completions: _IC, *, _debug: bool = False) -> _IC:
+ """
+ Rectify a set of completions to all have the same ``start`` and ``end``
+
+ .. warning::
+
+ Unstable
+
+ This function is unstable, API may change without warning.
+ It will also raise unless use in proper context manager.
+
+ Parameters
+ ----------
+ text : str
+ text that should be completed.
+ completions : Iterator[Completion]
+ iterator over the completions to rectify
+ _debug : bool
+ Log failed completion
+
+ Notes
+ -----
+ :any:`jedi.api.classes.Completion` s returned by Jedi may not have the same start and end, though
+ the Jupyter Protocol requires them to behave like so. This will readjust
+ the completion to have the same ``start`` and ``end`` by padding both
+ extremities with surrounding text.
+
+ During stabilisation should support a ``_debug`` option to log which
+ completion are return by the IPython completer and not found in Jedi in
+ order to make upstream bug report.
+ """
+ warnings.warn("`rectify_completions` is a provisional API (as of IPython 6.0). "
+ "It may change without warnings. "
+ "Use in corresponding context manager.",
+ category=ProvisionalCompleterWarning, stacklevel=2)
+
+ completions = list(completions)
+ if not completions:
+ return
+ starts = (c.start for c in completions)
+ ends = (c.end for c in completions)
+
+ new_start = min(starts)
+ new_end = max(ends)
+
+ seen_jedi = set()
+ seen_python_matches = set()
+ for c in completions:
+ new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
+ if c._origin == 'jedi':
+ seen_jedi.add(new_text)
+ elif c._origin == 'IPCompleter.python_matches':
+ seen_python_matches.add(new_text)
+ yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature)
+ diff = seen_python_matches.difference(seen_jedi)
+ if diff and _debug:
+ print('IPython.python matches have extras:', diff)
+
+
+if sys.platform == 'win32':
+ DELIMS = ' \t\n`!@#$^&*()=+[{]}|;\'",<>?'
+else:
+ DELIMS = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?'
+
+GREEDY_DELIMS = ' =\r\n'
+
+
+class CompletionSplitter(object):
+ """An object to split an input line in a manner similar to readline.
+
+ By having our own implementation, we can expose readline-like completion in
+ a uniform manner to all frontends. This object only needs to be given the
+ line of text to be split and the cursor position on said line, and it
+ returns the 'word' to be completed on at the cursor after splitting the
+ entire line.
+
+ What characters are used as splitting delimiters can be controlled by
+ setting the ``delims`` attribute (this is a property that internally
+ automatically builds the necessary regular expression)"""
+
+ # Private interface
+
+ # A string of delimiter characters. The default value makes sense for
+ # IPython's most typical usage patterns.
+ _delims = DELIMS
+
+ # The expression (a normal string) to be compiled into a regular expression
+ # for actual splitting. We store it as an attribute mostly for ease of
+ # debugging, since this type of code can be so tricky to debug.
+ _delim_expr = None
+
+ # The regular expression that does the actual splitting
+ _delim_re = None
+
+ def __init__(self, delims=None):
+ delims = CompletionSplitter._delims if delims is None else delims
+ self.delims = delims
+
+ @property
+ def delims(self):
+ """Return the string of delimiter characters."""
+ return self._delims
+
+ @delims.setter
+ def delims(self, delims):
+ """Set the delimiters for line splitting."""
+ expr = '[' + ''.join('\\'+ c for c in delims) + ']'
+ self._delim_re = re.compile(expr)
+ self._delims = delims
+ self._delim_expr = expr
+
+ def split_line(self, line, cursor_pos=None):
+ """Split a line of text with a cursor at the given position.
+ """
+ l = line if cursor_pos is None else line[:cursor_pos]
+ return self._delim_re.split(l)[-1]
+
+
+
+class Completer(Configurable):
+
+ greedy = Bool(
+ False,
+ help="""Activate greedy completion.
+
+ .. deprecated:: 8.8
+ Use :std:configtrait:`Completer.evaluation` and :std:configtrait:`Completer.auto_close_dict_keys` instead.
+
+ When enabled in IPython 8.8 or newer, changes configuration as follows:
+
+ - ``Completer.evaluation = 'unsafe'``
+ - ``Completer.auto_close_dict_keys = True``
+ """,
+ ).tag(config=True)
+
+ evaluation = Enum(
+ ("forbidden", "minimal", "limited", "unsafe", "dangerous"),
+ default_value="limited",
+ help="""Policy for code evaluation under completion.
+
+ Successive options allow to enable more eager evaluation for better
+ completion suggestions, including for nested dictionaries, nested lists,
+ or even results of function calls.
+ Setting ``unsafe`` or higher can lead to evaluation of arbitrary user
+ code on :kbd:`Tab` with potentially unwanted or dangerous side effects.
+
+ Allowed values are:
+
+ - ``forbidden``: no evaluation of code is permitted,
+ - ``minimal``: evaluation of literals and access to built-in namespace;
+ no item/attribute evaluationm no access to locals/globals,
+ no evaluation of any operations or comparisons.
+ - ``limited``: access to all namespaces, evaluation of hard-coded methods
+ (for example: :any:`dict.keys`, :any:`object.__getattr__`,
+ :any:`object.__getitem__`) on allow-listed objects (for example:
+ :any:`dict`, :any:`list`, :any:`tuple`, ``pandas.Series``),
+ - ``unsafe``: evaluation of all methods and function calls but not of
+ syntax with side-effects like `del x`,
+ - ``dangerous``: completely arbitrary evaluation.
+ """,
+ ).tag(config=True)
+
+ use_jedi = Bool(default_value=JEDI_INSTALLED,
+ help="Experimental: Use Jedi to generate autocompletions. "
+ "Default to True if jedi is installed.").tag(config=True)
+
+ jedi_compute_type_timeout = Int(default_value=400,
+ help="""Experimental: restrict time (in milliseconds) during which Jedi can compute types.
+ Set to 0 to stop computing types. Non-zero value lower than 100ms may hurt
+ performance by preventing jedi to build its cache.
+ """).tag(config=True)
+
+ debug = Bool(default_value=False,
+ help='Enable debug for the Completer. Mostly print extra '
+ 'information for experimental jedi integration.')\
+ .tag(config=True)
+
+ backslash_combining_completions = Bool(True,
+ help="Enable unicode completions, e.g. \\alpha . "
+ "Includes completion of latex commands, unicode names, and expanding "
+ "unicode characters back to latex commands.").tag(config=True)
+
+ auto_close_dict_keys = Bool(
+ False,
+ help="""
+ Enable auto-closing dictionary keys.
+
+ When enabled string keys will be suffixed with a final quote
+ (matching the opening quote), tuple keys will also receive a
+ separating comma if needed, and keys which are final will
+ receive a closing bracket (``]``).
+ """,
+ ).tag(config=True)
+
+ def __init__(self, namespace=None, global_namespace=None, **kwargs):
+ """Create a new completer for the command line.
+
+ Completer(namespace=ns, global_namespace=ns2) -> completer instance.
+
+ If unspecified, the default namespace where completions are performed
+ is __main__ (technically, __main__.__dict__). Namespaces should be
+ given as dictionaries.
+
+ An optional second namespace can be given. This allows the completer
+ to handle cases where both the local and global scopes need to be
+ distinguished.
+ """
+
+ # Don't bind to namespace quite yet, but flag whether the user wants a
+ # specific namespace or to use __main__.__dict__. This will allow us
+ # to bind to __main__.__dict__ at completion time, not now.
+ if namespace is None:
+ self.use_main_ns = True
+ else:
+ self.use_main_ns = False
+ self.namespace = namespace
+
+ # The global namespace, if given, can be bound directly
+ if global_namespace is None:
+ self.global_namespace = {}
+ else:
+ self.global_namespace = global_namespace
+
+ self.custom_matchers = []
+
+ super(Completer, self).__init__(**kwargs)
+
+ def complete(self, text, state):
+ """Return the next possible completion for 'text'.
+
+ This is called successively with state == 0, 1, 2, ... until it
+ returns None. The completion should begin with 'text'.
+
+ """
+ if self.use_main_ns:
+ self.namespace = __main__.__dict__
+
+ if state == 0:
+ if "." in text:
+ self.matches = self.attr_matches(text)
+ else:
+ self.matches = self.global_matches(text)
+ try:
+ return self.matches[state]
+ except IndexError:
+ return None
+
+ def global_matches(self, text):
+ """Compute matches when text is a simple name.
+
+ Return a list of all keywords, built-in functions and names currently
+ defined in self.namespace or self.global_namespace that match.
+
+ """
+ matches = []
+ match_append = matches.append
+ n = len(text)
+ for lst in [
+ keyword.kwlist,
+ builtin_mod.__dict__.keys(),
+ list(self.namespace.keys()),
+ list(self.global_namespace.keys()),
+ ]:
+ for word in lst:
+ if word[:n] == text and word != "__builtins__":
+ match_append(word)
+
+ snake_case_re = re.compile(r"[^_]+(_[^_]+)+?\Z")
+ for lst in [list(self.namespace.keys()), list(self.global_namespace.keys())]:
+ shortened = {
+ "_".join([sub[0] for sub in word.split("_")]): word
+ for word in lst
+ if snake_case_re.match(word)
+ }
+ for word in shortened.keys():
+ if word[:n] == text and word != "__builtins__":
+ match_append(shortened[word])
+ return matches
+
+ def attr_matches(self, text):
+ """Compute matches when text contains a dot.
+
+ Assuming the text is of the form NAME.NAME....[NAME], and is
+ evaluatable in self.namespace or self.global_namespace, it will be
+ evaluated and its attributes (as revealed by dir()) are used as
+ possible completions. (For class instances, class members are
+ also considered.)
+
+ WARNING: this can still invoke arbitrary C code, if an object
+ with a __getattr__ hook is evaluated.
+
+ """
+ m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
+ if not m2:
+ return []
+ expr, attr = m2.group(1, 2)
+
+ obj = self._evaluate_expr(expr)
+
+ if obj is not_found:
+ return []
+
+ if self.limit_to__all__ and hasattr(obj, '__all__'):
+ words = get__all__entries(obj)
+ else:
+ words = dir2(obj)
+
+ try:
+ words = generics.complete_object(obj, words)
+ except TryNext:
+ pass
+ except AssertionError:
+ raise
+ except Exception:
+ # Silence errors from completion function
+ #raise # dbg
+ pass
+ # Build match list to return
+ n = len(attr)
+ return ["%s.%s" % (expr, w) for w in words if w[:n] == attr]
+
+ def _evaluate_expr(self, expr):
+ obj = not_found
+ done = False
+ while not done and expr:
+ try:
+ obj = guarded_eval(
+ expr,
+ EvaluationContext(
+ globals=self.global_namespace,
+ locals=self.namespace,
+ evaluation=self.evaluation,
+ ),
+ )
+ done = True
+ except Exception as e:
+ if self.debug:
+ print("Evaluation exception", e)
+ # trim the expression to remove any invalid prefix
+ # e.g. user starts `(d[`, so we get `expr = '(d'`,
+ # where parenthesis is not closed.
+ # TODO: make this faster by reusing parts of the computation?
+ expr = expr[1:]
+ return obj
+
+def get__all__entries(obj):
+ """returns the strings in the __all__ attribute"""
+ try:
+ words = getattr(obj, '__all__')
+ except:
+ return []
+
+ return [w for w in words if isinstance(w, str)]
+
+
+class _DictKeyState(enum.Flag):
+ """Represent state of the key match in context of other possible matches.
+
+ - given `d1 = {'a': 1}` completion on `d1['` will yield `{'a': END_OF_ITEM}` as there is no tuple.
+ - given `d2 = {('a', 'b'): 1}`: `d2['a', '` will yield `{'b': END_OF_TUPLE}` as there is no tuple members to add beyond `'b'`.
+ - given `d3 = {('a', 'b'): 1}`: `d3['` will yield `{'a': IN_TUPLE}` as `'a'` can be added.
+ - given `d4 = {'a': 1, ('a', 'b'): 2}`: `d4['` will yield `{'a': END_OF_ITEM & END_OF_TUPLE}`
+ """
+
+ BASELINE = 0
+ END_OF_ITEM = enum.auto()
+ END_OF_TUPLE = enum.auto()
+ IN_TUPLE = enum.auto()
+
+
+def _parse_tokens(c):
+ """Parse tokens even if there is an error."""
+ tokens = []
+ token_generator = tokenize.generate_tokens(iter(c.splitlines()).__next__)
+ while True:
+ try:
+ tokens.append(next(token_generator))
+ except tokenize.TokenError:
+ return tokens
+ except StopIteration:
+ return tokens
+
+
+def _match_number_in_dict_key_prefix(prefix: str) -> Union[str, None]:
+ """Match any valid Python numeric literal in a prefix of dictionary keys.
+
+ References:
+ - https://docs.python.org/3/reference/lexical_analysis.html#numeric-literals
+ - https://docs.python.org/3/library/tokenize.html
+ """
+ if prefix[-1].isspace():
+ # if user typed a space we do not have anything to complete
+ # even if there was a valid number token before
+ return None
+ tokens = _parse_tokens(prefix)
+ rev_tokens = reversed(tokens)
+ skip_over = {tokenize.ENDMARKER, tokenize.NEWLINE}
+ number = None
+ for token in rev_tokens:
+ if token.type in skip_over:
+ continue
+ if number is None:
+ if token.type == tokenize.NUMBER:
+ number = token.string
+ continue
+ else:
+ # we did not match a number
+ return None
+ if token.type == tokenize.OP:
+ if token.string == ",":
+ break
+ if token.string in {"+", "-"}:
+ number = token.string + number
+ else:
+ return None
+ return number
+
+
+_INT_FORMATS = {
+ "0b": bin,
+ "0o": oct,
+ "0x": hex,
+}
+
+
+def match_dict_keys(
+ keys: List[Union[str, bytes, Tuple[Union[str, bytes], ...]]],
+ prefix: str,
+ delims: str,
+ extra_prefix: Optional[Tuple[Union[str, bytes], ...]] = None,
+) -> Tuple[str, int, Dict[str, _DictKeyState]]:
+ """Used by dict_key_matches, matching the prefix to a list of keys
+
+ Parameters
+ ----------
+ keys
+ list of keys in dictionary currently being completed.
+ prefix
+ Part of the text already typed by the user. E.g. `mydict[b'fo`
+ delims
+ String of delimiters to consider when finding the current key.
+ extra_prefix : optional
+ Part of the text already typed in multi-key index cases. E.g. for
+ `mydict['foo', "bar", 'b`, this would be `('foo', 'bar')`.
+
+ Returns
+ -------
+ A tuple of three elements: ``quote``, ``token_start``, ``matched``, with
+ ``quote`` being the quote that need to be used to close current string.
+ ``token_start`` the position where the replacement should start occurring,
+ ``matches`` a dictionary of replacement/completion keys on keys and values
+ indicating whether the state.
+ """
+ prefix_tuple = extra_prefix if extra_prefix else ()
+
+ prefix_tuple_size = sum(
+ [
+ # for pandas, do not count slices as taking space
+ not isinstance(k, slice)
+ for k in prefix_tuple
+ ]
+ )
+ text_serializable_types = (str, bytes, int, float, slice)
+
+ def filter_prefix_tuple(key):
+ # Reject too short keys
+ if len(key) <= prefix_tuple_size:
+ return False
+ # Reject keys which cannot be serialised to text
+ for k in key:
+ if not isinstance(k, text_serializable_types):
+ return False
+ # Reject keys that do not match the prefix
+ for k, pt in zip(key, prefix_tuple):
+ if k != pt and not isinstance(pt, slice):
+ return False
+ # All checks passed!
+ return True
+
+ filtered_key_is_final: Dict[
+ Union[str, bytes, int, float], _DictKeyState
+ ] = defaultdict(lambda: _DictKeyState.BASELINE)
+
+ for k in keys:
+ # If at least one of the matches is not final, mark as undetermined.
+ # This can happen with `d = {111: 'b', (111, 222): 'a'}` where
+ # `111` appears final on first match but is not final on the second.
+
+ if isinstance(k, tuple):
+ if filter_prefix_tuple(k):
+ key_fragment = k[prefix_tuple_size]
+ filtered_key_is_final[key_fragment] |= (
+ _DictKeyState.END_OF_TUPLE
+ if len(k) == prefix_tuple_size + 1
+ else _DictKeyState.IN_TUPLE
+ )
+ elif prefix_tuple_size > 0:
+ # we are completing a tuple but this key is not a tuple,
+ # so we should ignore it
+ pass
+ else:
+ if isinstance(k, text_serializable_types):
+ filtered_key_is_final[k] |= _DictKeyState.END_OF_ITEM
+
+ filtered_keys = filtered_key_is_final.keys()
+
+ if not prefix:
+ return "", 0, {repr(k): v for k, v in filtered_key_is_final.items()}
+
+ quote_match = re.search("(?:\"|')", prefix)
+ is_user_prefix_numeric = False
+
+ if quote_match:
+ quote = quote_match.group()
+ valid_prefix = prefix + quote
+ try:
+ prefix_str = literal_eval(valid_prefix)
+ except Exception:
+ return "", 0, {}
+ else:
+ # If it does not look like a string, let's assume
+ # we are dealing with a number or variable.
+ number_match = _match_number_in_dict_key_prefix(prefix)
+
+ # We do not want the key matcher to suggest variable names so we yield:
+ if number_match is None:
+ # The alternative would be to assume that user forgort the quote
+ # and if the substring matches, suggest adding it at the start.
+ return "", 0, {}
+
+ prefix_str = number_match
+ is_user_prefix_numeric = True
+ quote = ""
+
+ pattern = '[^' + ''.join('\\' + c for c in delims) + ']*$'
+ token_match = re.search(pattern, prefix, re.UNICODE)
+ assert token_match is not None # silence mypy
+ token_start = token_match.start()
+ token_prefix = token_match.group()
+
+ matched: Dict[str, _DictKeyState] = {}
+
+ str_key: Union[str, bytes]
+
+ for key in filtered_keys:
+ if isinstance(key, (int, float)):
+ # User typed a number but this key is not a number.
+ if not is_user_prefix_numeric:
+ continue
+ str_key = str(key)
+ if isinstance(key, int):
+ int_base = prefix_str[:2].lower()
+ # if user typed integer using binary/oct/hex notation:
+ if int_base in _INT_FORMATS:
+ int_format = _INT_FORMATS[int_base]
+ str_key = int_format(key)
+ else:
+ # User typed a string but this key is a number.
+ if is_user_prefix_numeric:
+ continue
+ str_key = key
+ try:
+ if not str_key.startswith(prefix_str):
+ continue
+ except (AttributeError, TypeError, UnicodeError) as e:
+ # Python 3+ TypeError on b'a'.startswith('a') or vice-versa
+ continue
+
+ # reformat remainder of key to begin with prefix
+ rem = str_key[len(prefix_str) :]
+ # force repr wrapped in '
+ rem_repr = repr(rem + '"') if isinstance(rem, str) else repr(rem + b'"')
+ rem_repr = rem_repr[1 + rem_repr.index("'"):-2]
+ if quote == '"':
+ # The entered prefix is quoted with ",
+ # but the match is quoted with '.
+ # A contained " hence needs escaping for comparison:
+ rem_repr = rem_repr.replace('"', '\\"')
+
+ # then reinsert prefix from start of token
+ match = "%s%s" % (token_prefix, rem_repr)
+
+ matched[match] = filtered_key_is_final[key]
+ return quote, token_start, matched
+
+
+def cursor_to_position(text:str, line:int, column:int)->int:
+ """
+ Convert the (line,column) position of the cursor in text to an offset in a
+ string.
+
+ Parameters
+ ----------
+ text : str
+ The text in which to calculate the cursor offset
+ line : int
+ Line of the cursor; 0-indexed
+ column : int
+ Column of the cursor 0-indexed
+
+ Returns
+ -------
+ Position of the cursor in ``text``, 0-indexed.
+
+ See Also
+ --------
+ position_to_cursor : reciprocal of this function
+
+ """
+ lines = text.split('\n')
+ assert line <= len(lines), '{} <= {}'.format(str(line), str(len(lines)))
+
+ return sum(len(l) + 1 for l in lines[:line]) + column
+
+def position_to_cursor(text:str, offset:int)->Tuple[int, int]:
+ """
+ Convert the position of the cursor in text (0 indexed) to a line
+ number(0-indexed) and a column number (0-indexed) pair
+
+ Position should be a valid position in ``text``.
+
+ Parameters
+ ----------
+ text : str
+ The text in which to calculate the cursor offset
+ offset : int
+ Position of the cursor in ``text``, 0-indexed.
+
+ Returns
+ -------
+ (line, column) : (int, int)
+ Line of the cursor; 0-indexed, column of the cursor 0-indexed
+
+ See Also
+ --------
+ cursor_to_position : reciprocal of this function
+
+ """
+
+ assert 0 <= offset <= len(text) , "0 <= %s <= %s" % (offset , len(text))
+
+ before = text[:offset]
+ blines = before.split('\n') # ! splitnes trim trailing \n
+ line = before.count('\n')
+ col = len(blines[-1])
+ return line, col
+
+
+def _safe_isinstance(obj, module, class_name, *attrs):
+ """Checks if obj is an instance of module.class_name if loaded
+ """
+ if module in sys.modules:
+ m = sys.modules[module]
+ for attr in [class_name, *attrs]:
+ m = getattr(m, attr)
+ return isinstance(obj, m)
+
+
+@context_matcher()
+def back_unicode_name_matcher(context: CompletionContext):
+ """Match Unicode characters back to Unicode name
+
+ Same as :any:`back_unicode_name_matches`, but adopted to new Matcher API.
+ """
+ fragment, matches = back_unicode_name_matches(context.text_until_cursor)
+ return _convert_matcher_v1_result_to_v2(
+ matches, type="unicode", fragment=fragment, suppress_if_matches=True
+ )
+
+
+def back_unicode_name_matches(text: str) -> Tuple[str, Sequence[str]]:
+ """Match Unicode characters back to Unicode name
+
+ This does ``☃`` -> ``\\snowman``
+
+ Note that snowman is not a valid python3 combining character but will be expanded.
+ Though it will not recombine back to the snowman character by the completion machinery.
+
+ This will not either back-complete standard sequences like \\n, \\b ...
+
+ .. deprecated:: 8.6
+ You can use :meth:`back_unicode_name_matcher` instead.
+
+ Returns
+ =======
+
+ Return a tuple with two elements:
+
+ - The Unicode character that was matched (preceded with a backslash), or
+ empty string,
+ - a sequence (of 1), name for the match Unicode character, preceded by
+ backslash, or empty if no match.
+ """
+ if len(text)<2:
+ return '', ()
+ maybe_slash = text[-2]
+ if maybe_slash != '\\':
+ return '', ()
+
+ char = text[-1]
+ # no expand on quote for completion in strings.
+ # nor backcomplete standard ascii keys
+ if char in string.ascii_letters or char in ('"',"'"):
+ return '', ()
+ try :
+ unic = unicodedata.name(char)
+ return '\\'+char,('\\'+unic,)
+ except KeyError:
+ pass
+ return '', ()
+
+
+@context_matcher()
+def back_latex_name_matcher(context: CompletionContext):
+ """Match latex characters back to unicode name
+
+ Same as :any:`back_latex_name_matches`, but adopted to new Matcher API.
+ """
+ fragment, matches = back_latex_name_matches(context.text_until_cursor)
+ return _convert_matcher_v1_result_to_v2(
+ matches, type="latex", fragment=fragment, suppress_if_matches=True
+ )
+
+
+def back_latex_name_matches(text: str) -> Tuple[str, Sequence[str]]:
+ """Match latex characters back to unicode name
+
+ This does ``\\ℵ`` -> ``\\aleph``
+
+ .. deprecated:: 8.6
+ You can use :meth:`back_latex_name_matcher` instead.
+ """
+ if len(text)<2:
+ return '', ()
+ maybe_slash = text[-2]
+ if maybe_slash != '\\':
+ return '', ()
+
+
+ char = text[-1]
+ # no expand on quote for completion in strings.
+ # nor backcomplete standard ascii keys
+ if char in string.ascii_letters or char in ('"',"'"):
+ return '', ()
+ try :
+ latex = reverse_latex_symbol[char]
+ # '\\' replace the \ as well
+ return '\\'+char,[latex]
+ except KeyError:
+ pass
+ return '', ()
+
+
+def _formatparamchildren(parameter) -> str:
+ """
+ Get parameter name and value from Jedi Private API
+
+ Jedi does not expose a simple way to get `param=value` from its API.
+
+ Parameters
+ ----------
+ parameter
+ Jedi's function `Param`
+
+ Returns
+ -------
+ A string like 'a', 'b=1', '*args', '**kwargs'
+
+ """
+ description = parameter.description
+ if not description.startswith('param '):
+ raise ValueError('Jedi function parameter description have change format.'
+ 'Expected "param ...", found %r".' % description)
+ return description[6:]
+
+def _make_signature(completion)-> str:
+ """
+ Make the signature from a jedi completion
+
+ Parameters
+ ----------
+ completion : jedi.Completion
+ object does not complete a function type
+
+ Returns
+ -------
+ a string consisting of the function signature, with the parenthesis but
+ without the function name. example:
+ `(a, *args, b=1, **kwargs)`
+
+ """
+
+ # it looks like this might work on jedi 0.17
+ if hasattr(completion, 'get_signatures'):
+ signatures = completion.get_signatures()
+ if not signatures:
+ return '(?)'
+
+ c0 = completion.get_signatures()[0]
+ return '('+c0.to_string().split('(', maxsplit=1)[1]
+
+ return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for signature in completion.get_signatures()
+ for p in signature.defined_names()) if f])
+
+
+_CompleteResult = Dict[str, MatcherResult]
+
+
+DICT_MATCHER_REGEX = re.compile(
+ r"""(?x)
+( # match dict-referring - or any get item object - expression
+ .+
+)
+\[ # open bracket
+\s* # and optional whitespace
+# Capture any number of serializable objects (e.g. "a", "b", 'c')
+# and slices
+((?:(?:
+ (?: # closed string
+ [uUbB]? # string prefix (r not handled)
+ (?:
+ '(?:[^']|(? SimpleMatcherResult:
+ """Utility to help with transition"""
+ result = {
+ "completions": [SimpleCompletion(text=match, type=type) for match in matches],
+ "suppress": (True if matches else False) if suppress_if_matches else False,
+ }
+ if fragment is not None:
+ result["matched_fragment"] = fragment
+ return cast(SimpleMatcherResult, result)
+
+
+class IPCompleter(Completer):
+ """Extension of the completer class with IPython-specific features"""
+
+ @observe('greedy')
+ def _greedy_changed(self, change):
+ """update the splitter and readline delims when greedy is changed"""
+ if change["new"]:
+ self.evaluation = "unsafe"
+ self.auto_close_dict_keys = True
+ self.splitter.delims = GREEDY_DELIMS
+ else:
+ self.evaluation = "limited"
+ self.auto_close_dict_keys = False
+ self.splitter.delims = DELIMS
+
+ dict_keys_only = Bool(
+ False,
+ help="""
+ Whether to show dict key matches only.
+
+ (disables all matchers except for `IPCompleter.dict_key_matcher`).
+ """,
+ )
+
+ suppress_competing_matchers = UnionTrait(
+ [Bool(allow_none=True), DictTrait(Bool(None, allow_none=True))],
+ default_value=None,
+ help="""
+ Whether to suppress completions from other *Matchers*.
+
+ When set to ``None`` (default) the matchers will attempt to auto-detect
+ whether suppression of other matchers is desirable. For example, at
+ the beginning of a line followed by `%` we expect a magic completion
+ to be the only applicable option, and after ``my_dict['`` we usually
+ expect a completion with an existing dictionary key.
+
+ If you want to disable this heuristic and see completions from all matchers,
+ set ``IPCompleter.suppress_competing_matchers = False``.
+ To disable the heuristic for specific matchers provide a dictionary mapping:
+ ``IPCompleter.suppress_competing_matchers = {'IPCompleter.dict_key_matcher': False}``.
+
+ Set ``IPCompleter.suppress_competing_matchers = True`` to limit
+ completions to the set of matchers with the highest priority;
+ this is equivalent to ``IPCompleter.merge_completions`` and
+ can be beneficial for performance, but will sometimes omit relevant
+ candidates from matchers further down the priority list.
+ """,
+ ).tag(config=True)
+
+ merge_completions = Bool(
+ True,
+ help="""Whether to merge completion results into a single list
+
+ If False, only the completion results from the first non-empty
+ completer will be returned.
+
+ As of version 8.6.0, setting the value to ``False`` is an alias for:
+ ``IPCompleter.suppress_competing_matchers = True.``.
+ """,
+ ).tag(config=True)
+
+ disable_matchers = ListTrait(
+ Unicode(),
+ help="""List of matchers to disable.
+
+ The list should contain matcher identifiers (see :any:`completion_matcher`).
+ """,
+ ).tag(config=True)
+
+ omit__names = Enum(
+ (0, 1, 2),
+ default_value=2,
+ help="""Instruct the completer to omit private method names
+
+ Specifically, when completing on ``object.``.
+
+ When 2 [default]: all names that start with '_' will be excluded.
+
+ When 1: all 'magic' names (``__foo__``) will be excluded.
+
+ When 0: nothing will be excluded.
+ """
+ ).tag(config=True)
+ limit_to__all__ = Bool(False,
+ help="""
+ DEPRECATED as of version 5.0.
+
+ Instruct the completer to use __all__ for the completion
+
+ Specifically, when completing on ``object.``.
+
+ When True: only those names in obj.__all__ will be included.
+
+ When False [default]: the __all__ attribute is ignored
+ """,
+ ).tag(config=True)
+
+ profile_completions = Bool(
+ default_value=False,
+ help="If True, emit profiling data for completion subsystem using cProfile."
+ ).tag(config=True)
+
+ profiler_output_dir = Unicode(
+ default_value=".completion_profiles",
+ help="Template for path at which to output profile data for completions."
+ ).tag(config=True)
+
+ @observe('limit_to__all__')
+ def _limit_to_all_changed(self, change):
+ warnings.warn('`IPython.core.IPCompleter.limit_to__all__` configuration '
+ 'value has been deprecated since IPython 5.0, will be made to have '
+ 'no effects and then removed in future version of IPython.',
+ UserWarning)
+
+ def __init__(
+ self, shell=None, namespace=None, global_namespace=None, config=None, **kwargs
+ ):
+ """IPCompleter() -> completer
+
+ Return a completer object.
+
+ Parameters
+ ----------
+ shell
+ a pointer to the ipython shell itself. This is needed
+ because this completer knows about magic functions, and those can
+ only be accessed via the ipython instance.
+ namespace : dict, optional
+ an optional dict where completions are performed.
+ global_namespace : dict, optional
+ secondary optional dict for completions, to
+ handle cases (such as IPython embedded inside functions) where
+ both Python scopes are visible.
+ config : Config
+ traitlet's config object
+ **kwargs
+ passed to super class unmodified.
+ """
+
+ self.magic_escape = ESC_MAGIC
+ self.splitter = CompletionSplitter()
+
+ # _greedy_changed() depends on splitter and readline being defined:
+ super().__init__(
+ namespace=namespace,
+ global_namespace=global_namespace,
+ config=config,
+ **kwargs,
+ )
+
+ # List where completion matches will be stored
+ self.matches = []
+ self.shell = shell
+ # Regexp to split filenames with spaces in them
+ self.space_name_re = re.compile(r'([^\\] )')
+ # Hold a local ref. to glob.glob for speed
+ self.glob = glob.glob
+
+ # Determine if we are running on 'dumb' terminals, like (X)Emacs
+ # buffers, to avoid completion problems.
+ term = os.environ.get('TERM','xterm')
+ self.dumb_terminal = term in ['dumb','emacs']
+
+ # Special handling of backslashes needed in win32 platforms
+ if sys.platform == "win32":
+ self.clean_glob = self._clean_glob_win32
+ else:
+ self.clean_glob = self._clean_glob
+
+ #regexp to parse docstring for function signature
+ self.docstring_sig_re = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
+ self.docstring_kwd_re = re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
+ #use this if positional argument name is also needed
+ #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)')
+
+ self.magic_arg_matchers = [
+ self.magic_config_matcher,
+ self.magic_color_matcher,
+ ]
+
+ # This is set externally by InteractiveShell
+ self.custom_completers = None
+
+ # This is a list of names of unicode characters that can be completed
+ # into their corresponding unicode value. The list is large, so we
+ # lazily initialize it on first use. Consuming code should access this
+ # attribute through the `@unicode_names` property.
+ self._unicode_names = None
+
+ self._backslash_combining_matchers = [
+ self.latex_name_matcher,
+ self.unicode_name_matcher,
+ back_latex_name_matcher,
+ back_unicode_name_matcher,
+ self.fwd_unicode_matcher,
+ ]
+
+ if not self.backslash_combining_completions:
+ for matcher in self._backslash_combining_matchers:
+ self.disable_matchers.append(_get_matcher_id(matcher))
+
+ if not self.merge_completions:
+ self.suppress_competing_matchers = True
+
+ @property
+ def matchers(self) -> List[Matcher]:
+ """All active matcher routines for completion"""
+ if self.dict_keys_only:
+ return [self.dict_key_matcher]
+
+ if self.use_jedi:
+ return [
+ *self.custom_matchers,
+ *self._backslash_combining_matchers,
+ *self.magic_arg_matchers,
+ self.custom_completer_matcher,
+ self.magic_matcher,
+ self._jedi_matcher,
+ self.dict_key_matcher,
+ self.file_matcher,
+ ]
+ else:
+ return [
+ *self.custom_matchers,
+ *self._backslash_combining_matchers,
+ *self.magic_arg_matchers,
+ self.custom_completer_matcher,
+ self.dict_key_matcher,
+ # TODO: convert python_matches to v2 API
+ self.magic_matcher,
+ self.python_matches,
+ self.file_matcher,
+ self.python_func_kw_matcher,
+ ]
+
+ def all_completions(self, text:str) -> List[str]:
+ """
+ Wrapper around the completion methods for the benefit of emacs.
+ """
+ prefix = text.rpartition('.')[0]
+ with provisionalcompleter():
+ return ['.'.join([prefix, c.text]) if prefix and self.use_jedi else c.text
+ for c in self.completions(text, len(text))]
+
+ return self.complete(text)[1]
+
+ def _clean_glob(self, text:str):
+ return self.glob("%s*" % text)
+
+ def _clean_glob_win32(self, text:str):
+ return [f.replace("\\","/")
+ for f in self.glob("%s*" % text)]
+
+ @context_matcher()
+ def file_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
+ """Same as :any:`file_matches`, but adopted to new Matcher API."""
+ matches = self.file_matches(context.token)
+ # TODO: add a heuristic for suppressing (e.g. if it has OS-specific delimiter,
+ # starts with `/home/`, `C:\`, etc)
+ return _convert_matcher_v1_result_to_v2(matches, type="path")
+
+ def file_matches(self, text: str) -> List[str]:
+ """Match filenames, expanding ~USER type strings.
+
+ Most of the seemingly convoluted logic in this completer is an
+ attempt to handle filenames with spaces in them. And yet it's not
+ quite perfect, because Python's readline doesn't expose all of the
+ GNU readline details needed for this to be done correctly.
+
+ For a filename with a space in it, the printed completions will be
+ only the parts after what's already been typed (instead of the
+ full completions, as is normally done). I don't think with the
+ current (as of Python 2.3) Python readline it's possible to do
+ better.
+
+ .. deprecated:: 8.6
+ You can use :meth:`file_matcher` instead.
+ """
+
+ # chars that require escaping with backslash - i.e. chars
+ # that readline treats incorrectly as delimiters, but we
+ # don't want to treat as delimiters in filename matching
+ # when escaped with backslash
+ if text.startswith('!'):
+ text = text[1:]
+ text_prefix = u'!'
+ else:
+ text_prefix = u''
+
+ text_until_cursor = self.text_until_cursor
+ # track strings with open quotes
+ open_quotes = has_open_quotes(text_until_cursor)
+
+ if '(' in text_until_cursor or '[' in text_until_cursor:
+ lsplit = text
+ else:
+ try:
+ # arg_split ~ shlex.split, but with unicode bugs fixed by us
+ lsplit = arg_split(text_until_cursor)[-1]
+ except ValueError:
+ # typically an unmatched ", or backslash without escaped char.
+ if open_quotes:
+ lsplit = text_until_cursor.split(open_quotes)[-1]
+ else:
+ return []
+ except IndexError:
+ # tab pressed on empty line
+ lsplit = ""
+
+ if not open_quotes and lsplit != protect_filename(lsplit):
+ # if protectables are found, do matching on the whole escaped name
+ has_protectables = True
+ text0,text = text,lsplit
+ else:
+ has_protectables = False
+ text = os.path.expanduser(text)
+
+ if text == "":
+ return [text_prefix + protect_filename(f) for f in self.glob("*")]
+
+ # Compute the matches from the filesystem
+ if sys.platform == 'win32':
+ m0 = self.clean_glob(text)
+ else:
+ m0 = self.clean_glob(text.replace('\\', ''))
+
+ if has_protectables:
+ # If we had protectables, we need to revert our changes to the
+ # beginning of filename so that we don't double-write the part
+ # of the filename we have so far
+ len_lsplit = len(lsplit)
+ matches = [text_prefix + text0 +
+ protect_filename(f[len_lsplit:]) for f in m0]
+ else:
+ if open_quotes:
+ # if we have a string with an open quote, we don't need to
+ # protect the names beyond the quote (and we _shouldn't_, as
+ # it would cause bugs when the filesystem call is made).
+ matches = m0 if sys.platform == "win32" else\
+ [protect_filename(f, open_quotes) for f in m0]
+ else:
+ matches = [text_prefix +
+ protect_filename(f) for f in m0]
+
+ # Mark directories in input list by appending '/' to their names.
+ return [x+'/' if os.path.isdir(x) else x for x in matches]
+
+ @context_matcher()
+ def magic_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
+ """Match magics."""
+ text = context.token
+ matches = self.magic_matches(text)
+ result = _convert_matcher_v1_result_to_v2(matches, type="magic")
+ is_magic_prefix = len(text) > 0 and text[0] == "%"
+ result["suppress"] = is_magic_prefix and bool(result["completions"])
+ return result
+
+ def magic_matches(self, text: str):
+ """Match magics.
+
+ .. deprecated:: 8.6
+ You can use :meth:`magic_matcher` instead.
+ """
+ # Get all shell magics now rather than statically, so magics loaded at
+ # runtime show up too.
+ lsm = self.shell.magics_manager.lsmagic()
+ line_magics = lsm['line']
+ cell_magics = lsm['cell']
+ pre = self.magic_escape
+ pre2 = pre+pre
+
+ explicit_magic = text.startswith(pre)
+
+ # Completion logic:
+ # - user gives %%: only do cell magics
+ # - user gives %: do both line and cell magics
+ # - no prefix: do both
+ # In other words, line magics are skipped if the user gives %% explicitly
+ #
+ # We also exclude magics that match any currently visible names:
+ # https://github.com/ipython/ipython/issues/4877, unless the user has
+ # typed a %:
+ # https://github.com/ipython/ipython/issues/10754
+ bare_text = text.lstrip(pre)
+ global_matches = self.global_matches(bare_text)
+ if not explicit_magic:
+ def matches(magic):
+ """
+ Filter magics, in particular remove magics that match
+ a name present in global namespace.
+ """
+ return ( magic.startswith(bare_text) and
+ magic not in global_matches )
+ else:
+ def matches(magic):
+ return magic.startswith(bare_text)
+
+ comp = [ pre2+m for m in cell_magics if matches(m)]
+ if not text.startswith(pre2):
+ comp += [ pre+m for m in line_magics if matches(m)]
+
+ return comp
+
+ @context_matcher()
+ def magic_config_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
+ """Match class names and attributes for %config magic."""
+ # NOTE: uses `line_buffer` equivalent for compatibility
+ matches = self.magic_config_matches(context.line_with_cursor)
+ return _convert_matcher_v1_result_to_v2(matches, type="param")
+
+ def magic_config_matches(self, text: str) -> List[str]:
+ """Match class names and attributes for %config magic.
+
+ .. deprecated:: 8.6
+ You can use :meth:`magic_config_matcher` instead.
+ """
+ texts = text.strip().split()
+
+ if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'):
+ # get all configuration classes
+ classes = sorted(set([ c for c in self.shell.configurables
+ if c.__class__.class_traits(config=True)
+ ]), key=lambda x: x.__class__.__name__)
+ classnames = [ c.__class__.__name__ for c in classes ]
+
+ # return all classnames if config or %config is given
+ if len(texts) == 1:
+ return classnames
+
+ # match classname
+ classname_texts = texts[1].split('.')
+ classname = classname_texts[0]
+ classname_matches = [ c for c in classnames
+ if c.startswith(classname) ]
+
+ # return matched classes or the matched class with attributes
+ if texts[1].find('.') < 0:
+ return classname_matches
+ elif len(classname_matches) == 1 and \
+ classname_matches[0] == classname:
+ cls = classes[classnames.index(classname)].__class__
+ help = cls.class_get_help()
+ # strip leading '--' from cl-args:
+ help = re.sub(re.compile(r'^--', re.MULTILINE), '', help)
+ return [ attr.split('=')[0]
+ for attr in help.strip().splitlines()
+ if attr.startswith(texts[1]) ]
+ return []
+
+ @context_matcher()
+ def magic_color_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
+ """Match color schemes for %colors magic."""
+ # NOTE: uses `line_buffer` equivalent for compatibility
+ matches = self.magic_color_matches(context.line_with_cursor)
+ return _convert_matcher_v1_result_to_v2(matches, type="param")
+
+ def magic_color_matches(self, text: str) -> List[str]:
+ """Match color schemes for %colors magic.
+
+ .. deprecated:: 8.6
+ You can use :meth:`magic_color_matcher` instead.
+ """
+ texts = text.split()
+ if text.endswith(' '):
+ # .split() strips off the trailing whitespace. Add '' back
+ # so that: '%colors ' -> ['%colors', '']
+ texts.append('')
+
+ if len(texts) == 2 and (texts[0] == 'colors' or texts[0] == '%colors'):
+ prefix = texts[1]
+ return [ color for color in InspectColors.keys()
+ if color.startswith(prefix) ]
+ return []
+
+ @context_matcher(identifier="IPCompleter.jedi_matcher")
+ def _jedi_matcher(self, context: CompletionContext) -> _JediMatcherResult:
+ matches = self._jedi_matches(
+ cursor_column=context.cursor_position,
+ cursor_line=context.cursor_line,
+ text=context.full_text,
+ )
+ return {
+ "completions": matches,
+ # static analysis should not suppress other matchers
+ "suppress": False,
+ }
+
+ def _jedi_matches(
+ self, cursor_column: int, cursor_line: int, text: str
+ ) -> Iterator[_JediCompletionLike]:
+ """
+ Return a list of :any:`jedi.api.Completion`s object from a ``text`` and
+ cursor position.
+
+ Parameters
+ ----------
+ cursor_column : int
+ column position of the cursor in ``text``, 0-indexed.
+ cursor_line : int
+ line position of the cursor in ``text``, 0-indexed
+ text : str
+ text to complete
+
+ Notes
+ -----
+ If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion`
+ object containing a string with the Jedi debug information attached.
+
+ .. deprecated:: 8.6
+ You can use :meth:`_jedi_matcher` instead.
+ """
+ namespaces = [self.namespace]
+ if self.global_namespace is not None:
+ namespaces.append(self.global_namespace)
+
+ completion_filter = lambda x:x
+ offset = cursor_to_position(text, cursor_line, cursor_column)
+ # filter output if we are completing for object members
+ if offset:
+ pre = text[offset-1]
+ if pre == '.':
+ if self.omit__names == 2:
+ completion_filter = lambda c:not c.name.startswith('_')
+ elif self.omit__names == 1:
+ completion_filter = lambda c:not (c.name.startswith('__') and c.name.endswith('__'))
+ elif self.omit__names == 0:
+ completion_filter = lambda x:x
+ else:
+ raise ValueError("Don't understand self.omit__names == {}".format(self.omit__names))
+
+ interpreter = jedi.Interpreter(text[:offset], namespaces)
+ try_jedi = True
+
+ try:
+ # find the first token in the current tree -- if it is a ' or " then we are in a string
+ completing_string = False
+ try:
+ first_child = next(c for c in interpreter._get_module().tree_node.children if hasattr(c, 'value'))
+ except StopIteration:
+ pass
+ else:
+ # note the value may be ', ", or it may also be ''' or """, or
+ # in some cases, """what/you/typed..., but all of these are
+ # strings.
+ completing_string = len(first_child.value) > 0 and first_child.value[0] in {"'", '"'}
+
+ # if we are in a string jedi is likely not the right candidate for
+ # now. Skip it.
+ try_jedi = not completing_string
+ except Exception as e:
+ # many of things can go wrong, we are using private API just don't crash.
+ if self.debug:
+ print("Error detecting if completing a non-finished string :", e, '|')
+
+ if not try_jedi:
+ return iter([])
+ try:
+ return filter(completion_filter, interpreter.complete(column=cursor_column, line=cursor_line + 1))
+ except Exception as e:
+ if self.debug:
+ return iter(
+ [
+ _FakeJediCompletion(
+ 'Oops Jedi has crashed, please report a bug with the following:\n"""\n%s\ns"""'
+ % (e)
+ )
+ ]
+ )
+ else:
+ return iter([])
+
+ @completion_matcher(api_version=1)
+ def python_matches(self, text: str) -> Iterable[str]:
+ """Match attributes or global python names"""
+ if "." in text:
+ try:
+ matches = self.attr_matches(text)
+ if text.endswith('.') and self.omit__names:
+ if self.omit__names == 1:
+ # true if txt is _not_ a __ name, false otherwise:
+ no__name = (lambda txt:
+ re.match(r'.*\.__.*?__',txt) is None)
+ else:
+ # true if txt is _not_ a _ name, false otherwise:
+ no__name = (lambda txt:
+ re.match(r'\._.*?',txt[txt.rindex('.'):]) is None)
+ matches = filter(no__name, matches)
+ except NameError:
+ # catches .
+ matches = []
+ else:
+ matches = self.global_matches(text)
+ return matches
+
+ def _default_arguments_from_docstring(self, doc):
+ """Parse the first line of docstring for call signature.
+
+ Docstring should be of the form 'min(iterable[, key=func])\n'.
+ It can also parse cython docstring of the form
+ 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)'.
+ """
+ if doc is None:
+ return []
+
+ #care only the firstline
+ line = doc.lstrip().splitlines()[0]
+
+ #p = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
+ #'min(iterable[, key=func])\n' -> 'iterable[, key=func]'
+ sig = self.docstring_sig_re.search(line)
+ if sig is None:
+ return []
+ # iterable[, key=func]' -> ['iterable[' ,' key=func]']
+ sig = sig.groups()[0].split(',')
+ ret = []
+ for s in sig:
+ #re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
+ ret += self.docstring_kwd_re.findall(s)
+ return ret
+
+ def _default_arguments(self, obj):
+ """Return the list of default arguments of obj if it is callable,
+ or empty list otherwise."""
+ call_obj = obj
+ ret = []
+ if inspect.isbuiltin(obj):
+ pass
+ elif not (inspect.isfunction(obj) or inspect.ismethod(obj)):
+ if inspect.isclass(obj):
+ #for cython embedsignature=True the constructor docstring
+ #belongs to the object itself not __init__
+ ret += self._default_arguments_from_docstring(
+ getattr(obj, '__doc__', ''))
+ # for classes, check for __init__,__new__
+ call_obj = (getattr(obj, '__init__', None) or
+ getattr(obj, '__new__', None))
+ # for all others, check if they are __call__able
+ elif hasattr(obj, '__call__'):
+ call_obj = obj.__call__
+ ret += self._default_arguments_from_docstring(
+ getattr(call_obj, '__doc__', ''))
+
+ _keeps = (inspect.Parameter.KEYWORD_ONLY,
+ inspect.Parameter.POSITIONAL_OR_KEYWORD)
+
+ try:
+ sig = inspect.signature(obj)
+ ret.extend(k for k, v in sig.parameters.items() if
+ v.kind in _keeps)
+ except ValueError:
+ pass
+
+ return list(set(ret))
+
+ @context_matcher()
+ def python_func_kw_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
+ """Match named parameters (kwargs) of the last open function."""
+ matches = self.python_func_kw_matches(context.token)
+ return _convert_matcher_v1_result_to_v2(matches, type="param")
+
+ def python_func_kw_matches(self, text):
+ """Match named parameters (kwargs) of the last open function.
+
+ .. deprecated:: 8.6
+ You can use :meth:`python_func_kw_matcher` instead.
+ """
+
+ if "." in text: # a parameter cannot be dotted
+ return []
+ try: regexp = self.__funcParamsRegex
+ except AttributeError:
+ regexp = self.__funcParamsRegex = re.compile(r'''
+ '.*?(?,a=1)", the candidate is "foo"
+ tokens = regexp.findall(self.text_until_cursor)
+ iterTokens = reversed(tokens); openPar = 0
+
+ for token in iterTokens:
+ if token == ')':
+ openPar -= 1
+ elif token == '(':
+ openPar += 1
+ if openPar > 0:
+ # found the last unclosed parenthesis
+ break
+ else:
+ return []
+ # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" )
+ ids = []
+ isId = re.compile(r'\w+$').match
+
+ while True:
+ try:
+ ids.append(next(iterTokens))
+ if not isId(ids[-1]):
+ ids.pop(); break
+ if not next(iterTokens) == '.':
+ break
+ except StopIteration:
+ break
+
+ # Find all named arguments already assigned to, as to avoid suggesting
+ # them again
+ usedNamedArgs = set()
+ par_level = -1
+ for token, next_token in zip(tokens, tokens[1:]):
+ if token == '(':
+ par_level += 1
+ elif token == ')':
+ par_level -= 1
+
+ if par_level != 0:
+ continue
+
+ if next_token != '=':
+ continue
+
+ usedNamedArgs.add(token)
+
+ argMatches = []
+ try:
+ callableObj = '.'.join(ids[::-1])
+ namedArgs = self._default_arguments(eval(callableObj,
+ self.namespace))
+
+ # Remove used named arguments from the list, no need to show twice
+ for namedArg in set(namedArgs) - usedNamedArgs:
+ if namedArg.startswith(text):
+ argMatches.append("%s=" %namedArg)
+ except:
+ pass
+
+ return argMatches
+
+ @staticmethod
+ def _get_keys(obj: Any) -> List[Any]:
+ # Objects can define their own completions by defining an
+ # _ipy_key_completions_() method.
+ method = get_real_method(obj, '_ipython_key_completions_')
+ if method is not None:
+ return method()
+
+ # Special case some common in-memory dict-like types
+ if isinstance(obj, dict) or _safe_isinstance(obj, "pandas", "DataFrame"):
+ try:
+ return list(obj.keys())
+ except Exception:
+ return []
+ elif _safe_isinstance(obj, "pandas", "core", "indexing", "_LocIndexer"):
+ try:
+ return list(obj.obj.keys())
+ except Exception:
+ return []
+ elif _safe_isinstance(obj, 'numpy', 'ndarray') or\
+ _safe_isinstance(obj, 'numpy', 'void'):
+ return obj.dtype.names or []
+ return []
+
+ @context_matcher()
+ def dict_key_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
+ """Match string keys in a dictionary, after e.g. ``foo[``."""
+ matches = self.dict_key_matches(context.token)
+ return _convert_matcher_v1_result_to_v2(
+ matches, type="dict key", suppress_if_matches=True
+ )
+
+ def dict_key_matches(self, text: str) -> List[str]:
+ """Match string keys in a dictionary, after e.g. ``foo[``.
+
+ .. deprecated:: 8.6
+ You can use :meth:`dict_key_matcher` instead.
+ """
+
+ # Short-circuit on closed dictionary (regular expression would
+ # not match anyway, but would take quite a while).
+ if self.text_until_cursor.strip().endswith("]"):
+ return []
+
+ match = DICT_MATCHER_REGEX.search(self.text_until_cursor)
+
+ if match is None:
+ return []
+
+ expr, prior_tuple_keys, key_prefix = match.groups()
+
+ obj = self._evaluate_expr(expr)
+
+ if obj is not_found:
+ return []
+
+ keys = self._get_keys(obj)
+ if not keys:
+ return keys
+
+ tuple_prefix = guarded_eval(
+ prior_tuple_keys,
+ EvaluationContext(
+ globals=self.global_namespace,
+ locals=self.namespace,
+ evaluation=self.evaluation,
+ in_subscript=True,
+ ),
+ )
+
+ closing_quote, token_offset, matches = match_dict_keys(
+ keys, key_prefix, self.splitter.delims, extra_prefix=tuple_prefix
+ )
+ if not matches:
+ return []
+
+ # get the cursor position of
+ # - the text being completed
+ # - the start of the key text
+ # - the start of the completion
+ text_start = len(self.text_until_cursor) - len(text)
+ if key_prefix:
+ key_start = match.start(3)
+ completion_start = key_start + token_offset
+ else:
+ key_start = completion_start = match.end()
+
+ # grab the leading prefix, to make sure all completions start with `text`
+ if text_start > key_start:
+ leading = ''
+ else:
+ leading = text[text_start:completion_start]
+
+ # append closing quote and bracket as appropriate
+ # this is *not* appropriate if the opening quote or bracket is outside
+ # the text given to this method, e.g. `d["""a\nt
+ can_close_quote = False
+ can_close_bracket = False
+
+ continuation = self.line_buffer[len(self.text_until_cursor) :].strip()
+
+ if continuation.startswith(closing_quote):
+ # do not close if already closed, e.g. `d['a'`
+ continuation = continuation[len(closing_quote) :]
+ else:
+ can_close_quote = True
+
+ continuation = continuation.strip()
+
+ # e.g. `pandas.DataFrame` has different tuple indexer behaviour,
+ # handling it is out of scope, so let's avoid appending suffixes.
+ has_known_tuple_handling = isinstance(obj, dict)
+
+ can_close_bracket = (
+ not continuation.startswith("]") and self.auto_close_dict_keys
+ )
+ can_close_tuple_item = (
+ not continuation.startswith(",")
+ and has_known_tuple_handling
+ and self.auto_close_dict_keys
+ )
+ can_close_quote = can_close_quote and self.auto_close_dict_keys
+
+ # fast path if closing qoute should be appended but not suffix is allowed
+ if not can_close_quote and not can_close_bracket and closing_quote:
+ return [leading + k for k in matches]
+
+ results = []
+
+ end_of_tuple_or_item = _DictKeyState.END_OF_TUPLE | _DictKeyState.END_OF_ITEM
+
+ for k, state_flag in matches.items():
+ result = leading + k
+ if can_close_quote and closing_quote:
+ result += closing_quote
+
+ if state_flag == end_of_tuple_or_item:
+ # We do not know which suffix to add,
+ # e.g. both tuple item and string
+ # match this item.
+ pass
+
+ if state_flag in end_of_tuple_or_item and can_close_bracket:
+ result += "]"
+ if state_flag == _DictKeyState.IN_TUPLE and can_close_tuple_item:
+ result += ", "
+ results.append(result)
+ return results
+
+ @context_matcher()
+ def unicode_name_matcher(self, context: CompletionContext):
+ """Same as :any:`unicode_name_matches`, but adopted to new Matcher API."""
+ fragment, matches = self.unicode_name_matches(context.text_until_cursor)
+ return _convert_matcher_v1_result_to_v2(
+ matches, type="unicode", fragment=fragment, suppress_if_matches=True
+ )
+
+ @staticmethod
+ def unicode_name_matches(text: str) -> Tuple[str, List[str]]:
+ """Match Latex-like syntax for unicode characters base
+ on the name of the character.
+
+ This does ``\\GREEK SMALL LETTER ETA`` -> ``η``
+
+ Works only on valid python 3 identifier, or on combining characters that
+ will combine to form a valid identifier.
+ """
+ slashpos = text.rfind('\\')
+ if slashpos > -1:
+ s = text[slashpos+1:]
+ try :
+ unic = unicodedata.lookup(s)
+ # allow combining chars
+ if ('a'+unic).isidentifier():
+ return '\\'+s,[unic]
+ except KeyError:
+ pass
+ return '', []
+
+ @context_matcher()
+ def latex_name_matcher(self, context: CompletionContext):
+ """Match Latex syntax for unicode characters.
+
+ This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
+ """
+ fragment, matches = self.latex_matches(context.text_until_cursor)
+ return _convert_matcher_v1_result_to_v2(
+ matches, type="latex", fragment=fragment, suppress_if_matches=True
+ )
+
+ def latex_matches(self, text: str) -> Tuple[str, Sequence[str]]:
+ """Match Latex syntax for unicode characters.
+
+ This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
+
+ .. deprecated:: 8.6
+ You can use :meth:`latex_name_matcher` instead.
+ """
+ slashpos = text.rfind('\\')
+ if slashpos > -1:
+ s = text[slashpos:]
+ if s in latex_symbols:
+ # Try to complete a full latex symbol to unicode
+ # \\alpha -> α
+ return s, [latex_symbols[s]]
+ else:
+ # If a user has partially typed a latex symbol, give them
+ # a full list of options \al -> [\aleph, \alpha]
+ matches = [k for k in latex_symbols if k.startswith(s)]
+ if matches:
+ return s, matches
+ return '', ()
+
+ @context_matcher()
+ def custom_completer_matcher(self, context):
+ """Dispatch custom completer.
+
+ If a match is found, suppresses all other matchers except for Jedi.
+ """
+ matches = self.dispatch_custom_completer(context.token) or []
+ result = _convert_matcher_v1_result_to_v2(
+ matches, type=_UNKNOWN_TYPE, suppress_if_matches=True
+ )
+ result["ordered"] = True
+ result["do_not_suppress"] = {_get_matcher_id(self._jedi_matcher)}
+ return result
+
+ def dispatch_custom_completer(self, text):
+ """
+ .. deprecated:: 8.6
+ You can use :meth:`custom_completer_matcher` instead.
+ """
+ if not self.custom_completers:
+ return
+
+ line = self.line_buffer
+ if not line.strip():
+ return None
+
+ # Create a little structure to pass all the relevant information about
+ # the current completion to any custom completer.
+ event = SimpleNamespace()
+ event.line = line
+ event.symbol = text
+ cmd = line.split(None,1)[0]
+ event.command = cmd
+ event.text_until_cursor = self.text_until_cursor
+
+ # for foo etc, try also to find completer for %foo
+ if not cmd.startswith(self.magic_escape):
+ try_magic = self.custom_completers.s_matches(
+ self.magic_escape + cmd)
+ else:
+ try_magic = []
+
+ for c in itertools.chain(self.custom_completers.s_matches(cmd),
+ try_magic,
+ self.custom_completers.flat_matches(self.text_until_cursor)):
+ try:
+ res = c(event)
+ if res:
+ # first, try case sensitive match
+ withcase = [r for r in res if r.startswith(text)]
+ if withcase:
+ return withcase
+ # if none, then case insensitive ones are ok too
+ text_low = text.lower()
+ return [r for r in res if r.lower().startswith(text_low)]
+ except TryNext:
+ pass
+ except KeyboardInterrupt:
+ """
+ If custom completer take too long,
+ let keyboard interrupt abort and return nothing.
+ """
+ break
+
+ return None
+
+ def completions(self, text: str, offset: int)->Iterator[Completion]:
+ """
+ Returns an iterator over the possible completions
+
+ .. warning::
+
+ Unstable
+
+ This function is unstable, API may change without warning.
+ It will also raise unless use in proper context manager.
+
+ Parameters
+ ----------
+ text : str
+ Full text of the current input, multi line string.
+ offset : int
+ Integer representing the position of the cursor in ``text``. Offset
+ is 0-based indexed.
+
+ Yields
+ ------
+ Completion
+
+ Notes
+ -----
+ The cursor on a text can either be seen as being "in between"
+ characters or "On" a character depending on the interface visible to
+ the user. For consistency the cursor being on "in between" characters X
+ and Y is equivalent to the cursor being "on" character Y, that is to say
+ the character the cursor is on is considered as being after the cursor.
+
+ Combining characters may span more that one position in the
+ text.
+
+ .. note::
+
+ If ``IPCompleter.debug`` is :any:`True` will yield a ``--jedi/ipython--``
+ fake Completion token to distinguish completion returned by Jedi
+ and usual IPython completion.
+
+ .. note::
+
+ Completions are not completely deduplicated yet. If identical
+ completions are coming from different sources this function does not
+ ensure that each completion object will only be present once.
+ """
+ warnings.warn("_complete is a provisional API (as of IPython 6.0). "
+ "It may change without warnings. "
+ "Use in corresponding context manager.",
+ category=ProvisionalCompleterWarning, stacklevel=2)
+
+ seen = set()
+ profiler:Optional[cProfile.Profile]
+ try:
+ if self.profile_completions:
+ import cProfile
+ profiler = cProfile.Profile()
+ profiler.enable()
+ else:
+ profiler = None
+
+ for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
+ if c and (c in seen):
+ continue
+ yield c
+ seen.add(c)
+ except KeyboardInterrupt:
+ """if completions take too long and users send keyboard interrupt,
+ do not crash and return ASAP. """
+ pass
+ finally:
+ if profiler is not None:
+ profiler.disable()
+ ensure_dir_exists(self.profiler_output_dir)
+ output_path = os.path.join(self.profiler_output_dir, str(uuid.uuid4()))
+ print("Writing profiler output to", output_path)
+ profiler.dump_stats(output_path)
+
+ def _completions(self, full_text: str, offset: int, *, _timeout) -> Iterator[Completion]:
+ """
+ Core completion module.Same signature as :any:`completions`, with the
+ extra `timeout` parameter (in seconds).
+
+ Computing jedi's completion ``.type`` can be quite expensive (it is a
+ lazy property) and can require some warm-up, more warm up than just
+ computing the ``name`` of a completion. The warm-up can be :
+
+ - Long warm-up the first time a module is encountered after
+ install/update: actually build parse/inference tree.
+
+ - first time the module is encountered in a session: load tree from
+ disk.
+
+ We don't want to block completions for tens of seconds so we give the
+ completer a "budget" of ``_timeout`` seconds per invocation to compute
+ completions types, the completions that have not yet been computed will
+ be marked as "unknown" an will have a chance to be computed next round
+ are things get cached.
+
+ Keep in mind that Jedi is not the only thing treating the completion so
+ keep the timeout short-ish as if we take more than 0.3 second we still
+ have lots of processing to do.
+
+ """
+ deadline = time.monotonic() + _timeout
+
+ before = full_text[:offset]
+ cursor_line, cursor_column = position_to_cursor(full_text, offset)
+
+ jedi_matcher_id = _get_matcher_id(self._jedi_matcher)
+
+ def is_non_jedi_result(
+ result: MatcherResult, identifier: str
+ ) -> TypeGuard[SimpleMatcherResult]:
+ return identifier != jedi_matcher_id
+
+ results = self._complete(
+ full_text=full_text, cursor_line=cursor_line, cursor_pos=cursor_column
+ )
+
+ non_jedi_results: Dict[str, SimpleMatcherResult] = {
+ identifier: result
+ for identifier, result in results.items()
+ if is_non_jedi_result(result, identifier)
+ }
+
+ jedi_matches = (
+ cast(_JediMatcherResult, results[jedi_matcher_id])["completions"]
+ if jedi_matcher_id in results
+ else ()
+ )
+
+ iter_jm = iter(jedi_matches)
+ if _timeout:
+ for jm in iter_jm:
+ try:
+ type_ = jm.type
+ except Exception:
+ if self.debug:
+ print("Error in Jedi getting type of ", jm)
+ type_ = None
+ delta = len(jm.name_with_symbols) - len(jm.complete)
+ if type_ == 'function':
+ signature = _make_signature(jm)
+ else:
+ signature = ''
+ yield Completion(start=offset - delta,
+ end=offset,
+ text=jm.name_with_symbols,
+ type=type_,
+ signature=signature,
+ _origin='jedi')
+
+ if time.monotonic() > deadline:
+ break
+
+ for jm in iter_jm:
+ delta = len(jm.name_with_symbols) - len(jm.complete)
+ yield Completion(
+ start=offset - delta,
+ end=offset,
+ text=jm.name_with_symbols,
+ type=_UNKNOWN_TYPE, # don't compute type for speed
+ _origin="jedi",
+ signature="",
+ )
+
+ # TODO:
+ # Suppress this, right now just for debug.
+ if jedi_matches and non_jedi_results and self.debug:
+ some_start_offset = before.rfind(
+ next(iter(non_jedi_results.values()))["matched_fragment"]
+ )
+ yield Completion(
+ start=some_start_offset,
+ end=offset,
+ text="--jedi/ipython--",
+ _origin="debug",
+ type="none",
+ signature="",
+ )
+
+ ordered: List[Completion] = []
+ sortable: List[Completion] = []
+
+ for origin, result in non_jedi_results.items():
+ matched_text = result["matched_fragment"]
+ start_offset = before.rfind(matched_text)
+ is_ordered = result.get("ordered", False)
+ container = ordered if is_ordered else sortable
+
+ # I'm unsure if this is always true, so let's assert and see if it
+ # crash
+ assert before.endswith(matched_text)
+
+ for simple_completion in result["completions"]:
+ completion = Completion(
+ start=start_offset,
+ end=offset,
+ text=simple_completion.text,
+ _origin=origin,
+ signature="",
+ type=simple_completion.type or _UNKNOWN_TYPE,
+ )
+ container.append(completion)
+
+ yield from list(self._deduplicate(ordered + self._sort(sortable)))[
+ :MATCHES_LIMIT
+ ]
+
+ def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]:
+ """Find completions for the given text and line context.
+
+ Note that both the text and the line_buffer are optional, but at least
+ one of them must be given.
+
+ Parameters
+ ----------
+ text : string, optional
+ Text to perform the completion on. If not given, the line buffer
+ is split using the instance's CompletionSplitter object.
+ line_buffer : string, optional
+ If not given, the completer attempts to obtain the current line
+ buffer via readline. This keyword allows clients which are
+ requesting for text completions in non-readline contexts to inform
+ the completer of the entire text.
+ cursor_pos : int, optional
+ Index of the cursor in the full line buffer. Should be provided by
+ remote frontends where kernel has no access to frontend state.
+
+ Returns
+ -------
+ Tuple of two items:
+ text : str
+ Text that was actually used in the completion.
+ matches : list
+ A list of completion matches.
+
+ Notes
+ -----
+ This API is likely to be deprecated and replaced by
+ :any:`IPCompleter.completions` in the future.
+
+ """
+ warnings.warn('`Completer.complete` is pending deprecation since '
+ 'IPython 6.0 and will be replaced by `Completer.completions`.',
+ PendingDeprecationWarning)
+ # potential todo, FOLD the 3rd throw away argument of _complete
+ # into the first 2 one.
+ # TODO: Q: does the above refer to jedi completions (i.e. 0-indexed?)
+ # TODO: should we deprecate now, or does it stay?
+
+ results = self._complete(
+ line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0
+ )
+
+ jedi_matcher_id = _get_matcher_id(self._jedi_matcher)
+
+ return self._arrange_and_extract(
+ results,
+ # TODO: can we confirm that excluding Jedi here was a deliberate choice in previous version?
+ skip_matchers={jedi_matcher_id},
+ # this API does not support different start/end positions (fragments of token).
+ abort_if_offset_changes=True,
+ )
+
+ def _arrange_and_extract(
+ self,
+ results: Dict[str, MatcherResult],
+ skip_matchers: Set[str],
+ abort_if_offset_changes: bool,
+ ):
+
+ sortable: List[AnyMatcherCompletion] = []
+ ordered: List[AnyMatcherCompletion] = []
+ most_recent_fragment = None
+ for identifier, result in results.items():
+ if identifier in skip_matchers:
+ continue
+ if not result["completions"]:
+ continue
+ if not most_recent_fragment:
+ most_recent_fragment = result["matched_fragment"]
+ if (
+ abort_if_offset_changes
+ and result["matched_fragment"] != most_recent_fragment
+ ):
+ break
+ if result.get("ordered", False):
+ ordered.extend(result["completions"])
+ else:
+ sortable.extend(result["completions"])
+
+ if not most_recent_fragment:
+ most_recent_fragment = "" # to satisfy typechecker (and just in case)
+
+ return most_recent_fragment, [
+ m.text for m in self._deduplicate(ordered + self._sort(sortable))
+ ]
+
+ def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
+ full_text=None) -> _CompleteResult:
+ """
+ Like complete but can also returns raw jedi completions as well as the
+ origin of the completion text. This could (and should) be made much
+ cleaner but that will be simpler once we drop the old (and stateful)
+ :any:`complete` API.
+
+ With current provisional API, cursor_pos act both (depending on the
+ caller) as the offset in the ``text`` or ``line_buffer``, or as the
+ ``column`` when passing multiline strings this could/should be renamed
+ but would add extra noise.
+
+ Parameters
+ ----------
+ cursor_line
+ Index of the line the cursor is on. 0 indexed.
+ cursor_pos
+ Position of the cursor in the current line/line_buffer/text. 0
+ indexed.
+ line_buffer : optional, str
+ The current line the cursor is in, this is mostly due to legacy
+ reason that readline could only give a us the single current line.
+ Prefer `full_text`.
+ text : str
+ The current "token" the cursor is in, mostly also for historical
+ reasons. as the completer would trigger only after the current line
+ was parsed.
+ full_text : str
+ Full text of the current cell.
+
+ Returns
+ -------
+ An ordered dictionary where keys are identifiers of completion
+ matchers and values are ``MatcherResult``s.
+ """
+
+ # if the cursor position isn't given, the only sane assumption we can
+ # make is that it's at the end of the line (the common case)
+ if cursor_pos is None:
+ cursor_pos = len(line_buffer) if text is None else len(text)
+
+ if self.use_main_ns:
+ self.namespace = __main__.__dict__
+
+ # if text is either None or an empty string, rely on the line buffer
+ if (not line_buffer) and full_text:
+ line_buffer = full_text.split('\n')[cursor_line]
+ if not text: # issue #11508: check line_buffer before calling split_line
+ text = (
+ self.splitter.split_line(line_buffer, cursor_pos) if line_buffer else ""
+ )
+
+ # If no line buffer is given, assume the input text is all there was
+ if line_buffer is None:
+ line_buffer = text
+
+ # deprecated - do not use `line_buffer` in new code.
+ self.line_buffer = line_buffer
+ self.text_until_cursor = self.line_buffer[:cursor_pos]
+
+ if not full_text:
+ full_text = line_buffer
+
+ context = CompletionContext(
+ full_text=full_text,
+ cursor_position=cursor_pos,
+ cursor_line=cursor_line,
+ token=text,
+ limit=MATCHES_LIMIT,
+ )
+
+ # Start with a clean slate of completions
+ results: Dict[str, MatcherResult] = {}
+
+ jedi_matcher_id = _get_matcher_id(self._jedi_matcher)
+
+ suppressed_matchers: Set[str] = set()
+
+ matchers = {
+ _get_matcher_id(matcher): matcher
+ for matcher in sorted(
+ self.matchers, key=_get_matcher_priority, reverse=True
+ )
+ }
+
+ for matcher_id, matcher in matchers.items():
+ matcher_id = _get_matcher_id(matcher)
+
+ if matcher_id in self.disable_matchers:
+ continue
+
+ if matcher_id in results:
+ warnings.warn(f"Duplicate matcher ID: {matcher_id}.")
+
+ if matcher_id in suppressed_matchers:
+ continue
+
+ result: MatcherResult
+ try:
+ if _is_matcher_v1(matcher):
+ result = _convert_matcher_v1_result_to_v2(
+ matcher(text), type=_UNKNOWN_TYPE
+ )
+ elif _is_matcher_v2(matcher):
+ result = matcher(context)
+ else:
+ api_version = _get_matcher_api_version(matcher)
+ raise ValueError(f"Unsupported API version {api_version}")
+ except:
+ # Show the ugly traceback if the matcher causes an
+ # exception, but do NOT crash the kernel!
+ sys.excepthook(*sys.exc_info())
+ continue
+
+ # set default value for matched fragment if suffix was not selected.
+ result["matched_fragment"] = result.get("matched_fragment", context.token)
+
+ if not suppressed_matchers:
+ suppression_recommended: Union[bool, Set[str]] = result.get(
+ "suppress", False
+ )
+
+ suppression_config = (
+ self.suppress_competing_matchers.get(matcher_id, None)
+ if isinstance(self.suppress_competing_matchers, dict)
+ else self.suppress_competing_matchers
+ )
+ should_suppress = (
+ (suppression_config is True)
+ or (suppression_recommended and (suppression_config is not False))
+ ) and has_any_completions(result)
+
+ if should_suppress:
+ suppression_exceptions: Set[str] = result.get(
+ "do_not_suppress", set()
+ )
+ if isinstance(suppression_recommended, Iterable):
+ to_suppress = set(suppression_recommended)
+ else:
+ to_suppress = set(matchers)
+ suppressed_matchers = to_suppress - suppression_exceptions
+
+ new_results = {}
+ for previous_matcher_id, previous_result in results.items():
+ if previous_matcher_id not in suppressed_matchers:
+ new_results[previous_matcher_id] = previous_result
+ results = new_results
+
+ results[matcher_id] = result
+
+ _, matches = self._arrange_and_extract(
+ results,
+ # TODO Jedi completions non included in legacy stateful API; was this deliberate or omission?
+ # if it was omission, we can remove the filtering step, otherwise remove this comment.
+ skip_matchers={jedi_matcher_id},
+ abort_if_offset_changes=False,
+ )
+
+ # populate legacy stateful API
+ self.matches = matches
+
+ return results
+
+ @staticmethod
+ def _deduplicate(
+ matches: Sequence[AnyCompletion],
+ ) -> Iterable[AnyCompletion]:
+ filtered_matches: Dict[str, AnyCompletion] = {}
+ for match in matches:
+ text = match.text
+ if (
+ text not in filtered_matches
+ or filtered_matches[text].type == _UNKNOWN_TYPE
+ ):
+ filtered_matches[text] = match
+
+ return filtered_matches.values()
+
+ @staticmethod
+ def _sort(matches: Sequence[AnyCompletion]):
+ return sorted(matches, key=lambda x: completions_sorting_key(x.text))
+
+ @context_matcher()
+ def fwd_unicode_matcher(self, context: CompletionContext):
+ """Same as :any:`fwd_unicode_match`, but adopted to new Matcher API."""
+ # TODO: use `context.limit` to terminate early once we matched the maximum
+ # number that will be used downstream; can be added as an optional to
+ # `fwd_unicode_match(text: str, limit: int = None)` or we could re-implement here.
+ fragment, matches = self.fwd_unicode_match(context.text_until_cursor)
+ return _convert_matcher_v1_result_to_v2(
+ matches, type="unicode", fragment=fragment, suppress_if_matches=True
+ )
+
+ def fwd_unicode_match(self, text: str) -> Tuple[str, Sequence[str]]:
+ """
+ Forward match a string starting with a backslash with a list of
+ potential Unicode completions.
+
+ Will compute list of Unicode character names on first call and cache it.
+
+ .. deprecated:: 8.6
+ You can use :meth:`fwd_unicode_matcher` instead.
+
+ Returns
+ -------
+ At tuple with:
+ - matched text (empty if no matches)
+ - list of potential completions, empty tuple otherwise)
+ """
+ # TODO: self.unicode_names is here a list we traverse each time with ~100k elements.
+ # We could do a faster match using a Trie.
+
+ # Using pygtrie the following seem to work:
+
+ # s = PrefixSet()
+
+ # for c in range(0,0x10FFFF + 1):
+ # try:
+ # s.add(unicodedata.name(chr(c)))
+ # except ValueError:
+ # pass
+ # [''.join(k) for k in s.iter(prefix)]
+
+ # But need to be timed and adds an extra dependency.
+
+ slashpos = text.rfind('\\')
+ # if text starts with slash
+ if slashpos > -1:
+ # PERF: It's important that we don't access self._unicode_names
+ # until we're inside this if-block. _unicode_names is lazily
+ # initialized, and it takes a user-noticeable amount of time to
+ # initialize it, so we don't want to initialize it unless we're
+ # actually going to use it.
+ s = text[slashpos + 1 :]
+ sup = s.upper()
+ candidates = [x for x in self.unicode_names if x.startswith(sup)]
+ if candidates:
+ return s, candidates
+ candidates = [x for x in self.unicode_names if sup in x]
+ if candidates:
+ return s, candidates
+ splitsup = sup.split(" ")
+ candidates = [
+ x for x in self.unicode_names if all(u in x for u in splitsup)
+ ]
+ if candidates:
+ return s, candidates
+
+ return "", ()
+
+ # if text does not start with slash
+ else:
+ return '', ()
+
+ @property
+ def unicode_names(self) -> List[str]:
+ """List of names of unicode code points that can be completed.
+
+ The list is lazily initialized on first access.
+ """
+ if self._unicode_names is None:
+ names = []
+ for c in range(0,0x10FFFF + 1):
+ try:
+ names.append(unicodedata.name(chr(c)))
+ except ValueError:
+ pass
+ self._unicode_names = _unicode_name_compute(_UNICODE_RANGES)
+
+ return self._unicode_names
+
+def _unicode_name_compute(ranges:List[Tuple[int,int]]) -> List[str]:
+ names = []
+ for start,stop in ranges:
+ for c in range(start, stop) :
+ try:
+ names.append(unicodedata.name(chr(c)))
+ except ValueError:
+ pass
+ return names
diff --git a/venv/lib/python3.10/site-packages/IPython/core/completerlib.py b/venv/lib/python3.10/site-packages/IPython/core/completerlib.py
new file mode 100644
index 000000000..0ca97e7b7
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/core/completerlib.py
@@ -0,0 +1,370 @@
+# encoding: utf-8
+"""Implementations for various useful completers.
+
+These are all loaded by default by IPython.
+"""
+#-----------------------------------------------------------------------------
+# Copyright (C) 2010-2011 The IPython Development Team.
+#
+# Distributed under the terms of the BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+
+# Stdlib imports
+import glob
+import inspect
+import os
+import re
+import sys
+from importlib import import_module
+from importlib.machinery import all_suffixes
+
+
+# Third-party imports
+from time import time
+from zipimport import zipimporter
+
+# Our own imports
+from .completer import expand_user, compress_user
+from .error import TryNext
+from ..utils._process_common import arg_split
+
+# FIXME: this should be pulled in with the right call via the component system
+from IPython import get_ipython
+
+from typing import List
+
+#-----------------------------------------------------------------------------
+# Globals and constants
+#-----------------------------------------------------------------------------
+_suffixes = all_suffixes()
+
+# Time in seconds after which the rootmodules will be stored permanently in the
+# ipython ip.db database (kept in the user's .ipython dir).
+TIMEOUT_STORAGE = 2
+
+# Time in seconds after which we give up
+TIMEOUT_GIVEUP = 20
+
+# Regular expression for the python import statement
+import_re = re.compile(r'(?P[^\W\d]\w*?)'
+ r'(?P[/\\]__init__)?'
+ r'(?P%s)$' %
+ r'|'.join(re.escape(s) for s in _suffixes))
+
+# RE for the ipython %run command (python + ipython scripts)
+magic_run_re = re.compile(r'.*(\.ipy|\.ipynb|\.py[w]?)$')
+
+#-----------------------------------------------------------------------------
+# Local utilities
+#-----------------------------------------------------------------------------
+
+def module_list(path):
+ """
+ Return the list containing the names of the modules available in the given
+ folder.
+ """
+ # sys.path has the cwd as an empty string, but isdir/listdir need it as '.'
+ if path == '':
+ path = '.'
+
+ # A few local constants to be used in loops below
+ pjoin = os.path.join
+
+ if os.path.isdir(path):
+ # Build a list of all files in the directory and all files
+ # in its subdirectories. For performance reasons, do not
+ # recurse more than one level into subdirectories.
+ files = []
+ for root, dirs, nondirs in os.walk(path, followlinks=True):
+ subdir = root[len(path)+1:]
+ if subdir:
+ files.extend(pjoin(subdir, f) for f in nondirs)
+ dirs[:] = [] # Do not recurse into additional subdirectories.
+ else:
+ files.extend(nondirs)
+
+ else:
+ try:
+ files = list(zipimporter(path)._files.keys())
+ except:
+ files = []
+
+ # Build a list of modules which match the import_re regex.
+ modules = []
+ for f in files:
+ m = import_re.match(f)
+ if m:
+ modules.append(m.group('name'))
+ return list(set(modules))
+
+
+def get_root_modules():
+ """
+ Returns a list containing the names of all the modules available in the
+ folders of the pythonpath.
+
+ ip.db['rootmodules_cache'] maps sys.path entries to list of modules.
+ """
+ ip = get_ipython()
+ if ip is None:
+ # No global shell instance to store cached list of modules.
+ # Don't try to scan for modules every time.
+ return list(sys.builtin_module_names)
+
+ rootmodules_cache = ip.db.get('rootmodules_cache', {})
+ rootmodules = list(sys.builtin_module_names)
+ start_time = time()
+ store = False
+ for path in sys.path:
+ try:
+ modules = rootmodules_cache[path]
+ except KeyError:
+ modules = module_list(path)
+ try:
+ modules.remove('__init__')
+ except ValueError:
+ pass
+ if path not in ('', '.'): # cwd modules should not be cached
+ rootmodules_cache[path] = modules
+ if time() - start_time > TIMEOUT_STORAGE and not store:
+ store = True
+ print("\nCaching the list of root modules, please wait!")
+ print("(This will only be done once - type '%rehashx' to "
+ "reset cache!)\n")
+ sys.stdout.flush()
+ if time() - start_time > TIMEOUT_GIVEUP:
+ print("This is taking too long, we give up.\n")
+ return []
+ rootmodules.extend(modules)
+ if store:
+ ip.db['rootmodules_cache'] = rootmodules_cache
+ rootmodules = list(set(rootmodules))
+ return rootmodules
+
+
+def is_importable(module, attr, only_modules):
+ if only_modules:
+ return inspect.ismodule(getattr(module, attr))
+ else:
+ return not(attr[:2] == '__' and attr[-2:] == '__')
+
+def is_possible_submodule(module, attr):
+ try:
+ obj = getattr(module, attr)
+ except AttributeError:
+ # Is possilby an unimported submodule
+ return True
+ except TypeError:
+ # https://github.com/ipython/ipython/issues/9678
+ return False
+ return inspect.ismodule(obj)
+
+
+def try_import(mod: str, only_modules=False) -> List[str]:
+ """
+ Try to import given module and return list of potential completions.
+ """
+ mod = mod.rstrip('.')
+ try:
+ m = import_module(mod)
+ except:
+ return []
+
+ m_is_init = '__init__' in (getattr(m, '__file__', '') or '')
+
+ completions = []
+ if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
+ completions.extend( [attr for attr in dir(m) if
+ is_importable(m, attr, only_modules)])
+
+ m_all = getattr(m, "__all__", [])
+ if only_modules:
+ completions.extend(attr for attr in m_all if is_possible_submodule(m, attr))
+ else:
+ completions.extend(m_all)
+
+ if m_is_init:
+ completions.extend(module_list(os.path.dirname(m.__file__)))
+ completions_set = {c for c in completions if isinstance(c, str)}
+ completions_set.discard('__init__')
+ return list(completions_set)
+
+
+#-----------------------------------------------------------------------------
+# Completion-related functions.
+#-----------------------------------------------------------------------------
+
+def quick_completer(cmd, completions):
+ r""" Easily create a trivial completer for a command.
+
+ Takes either a list of completions, or all completions in string (that will
+ be split on whitespace).
+
+ Example::
+
+ [d:\ipython]|1> import ipy_completers
+ [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
+ [d:\ipython]|3> foo b
+ bar baz
+ [d:\ipython]|3> foo ba
+ """
+
+ if isinstance(completions, str):
+ completions = completions.split()
+
+ def do_complete(self, event):
+ return completions
+
+ get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
+
+def module_completion(line):
+ """
+ Returns a list containing the completion possibilities for an import line.
+
+ The line looks like this :
+ 'import xml.d'
+ 'from xml.dom import'
+ """
+
+ words = line.split(' ')
+ nwords = len(words)
+
+ # from whatever -> 'import '
+ if nwords == 3 and words[0] == 'from':
+ return ['import ']
+
+ # 'from xy' or 'import xy'
+ if nwords < 3 and (words[0] in {'%aimport', 'import', 'from'}) :
+ if nwords == 1:
+ return get_root_modules()
+ mod = words[1].split('.')
+ if len(mod) < 2:
+ return get_root_modules()
+ completion_list = try_import('.'.join(mod[:-1]), True)
+ return ['.'.join(mod[:-1] + [el]) for el in completion_list]
+
+ # 'from xyz import abc'
+ if nwords >= 3 and words[0] == 'from':
+ mod = words[1]
+ return try_import(mod)
+
+#-----------------------------------------------------------------------------
+# Completers
+#-----------------------------------------------------------------------------
+# These all have the func(self, event) signature to be used as custom
+# completers
+
+def module_completer(self,event):
+ """Give completions after user has typed 'import ...' or 'from ...'"""
+
+ # This works in all versions of python. While 2.5 has
+ # pkgutil.walk_packages(), that particular routine is fairly dangerous,
+ # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
+ # of possibly problematic side effects.
+ # This search the folders in the sys.path for available modules.
+
+ return module_completion(event.line)
+
+# FIXME: there's a lot of logic common to the run, cd and builtin file
+# completers, that is currently reimplemented in each.
+
+def magic_run_completer(self, event):
+ """Complete files that end in .py or .ipy or .ipynb for the %run command.
+ """
+ comps = arg_split(event.line, strict=False)
+ # relpath should be the current token that we need to complete.
+ if (len(comps) > 1) and (not event.line.endswith(' ')):
+ relpath = comps[-1].strip("'\"")
+ else:
+ relpath = ''
+
+ #print("\nev=", event) # dbg
+ #print("rp=", relpath) # dbg
+ #print('comps=', comps) # dbg
+
+ lglob = glob.glob
+ isdir = os.path.isdir
+ relpath, tilde_expand, tilde_val = expand_user(relpath)
+
+ # Find if the user has already typed the first filename, after which we
+ # should complete on all files, since after the first one other files may
+ # be arguments to the input script.
+
+ if any(magic_run_re.match(c) for c in comps):
+ matches = [f.replace('\\','/') + ('/' if isdir(f) else '')
+ for f in lglob(relpath+'*')]
+ else:
+ dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
+ pys = [f.replace('\\','/')
+ for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
+ lglob(relpath+'*.ipynb') + lglob(relpath + '*.pyw')]
+
+ matches = dirs + pys
+
+ #print('run comp:', dirs+pys) # dbg
+ return [compress_user(p, tilde_expand, tilde_val) for p in matches]
+
+
+def cd_completer(self, event):
+ """Completer function for cd, which only returns directories."""
+ ip = get_ipython()
+ relpath = event.symbol
+
+ #print(event) # dbg
+ if event.line.endswith('-b') or ' -b ' in event.line:
+ # return only bookmark completions
+ bkms = self.db.get('bookmarks', None)
+ if bkms:
+ return bkms.keys()
+ else:
+ return []
+
+ if event.symbol == '-':
+ width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
+ # jump in directory history by number
+ fmt = '-%0' + width_dh +'d [%s]'
+ ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
+ if len(ents) > 1:
+ return ents
+ return []
+
+ if event.symbol.startswith('--'):
+ return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
+
+ # Expand ~ in path and normalize directory separators.
+ relpath, tilde_expand, tilde_val = expand_user(relpath)
+ relpath = relpath.replace('\\','/')
+
+ found = []
+ for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
+ if os.path.isdir(f)]:
+ if ' ' in d:
+ # we don't want to deal with any of that, complex code
+ # for this is elsewhere
+ raise TryNext
+
+ found.append(d)
+
+ if not found:
+ if os.path.isdir(relpath):
+ return [compress_user(relpath, tilde_expand, tilde_val)]
+
+ # if no completions so far, try bookmarks
+ bks = self.db.get('bookmarks',{})
+ bkmatches = [s for s in bks if s.startswith(event.symbol)]
+ if bkmatches:
+ return bkmatches
+
+ raise TryNext
+
+ return [compress_user(p, tilde_expand, tilde_val) for p in found]
+
+def reset_completer(self, event):
+ "A completer for %reset magic"
+ return '-f -s in out array dhist'.split()
diff --git a/venv/lib/python3.10/site-packages/IPython/core/crashhandler.py b/venv/lib/python3.10/site-packages/IPython/core/crashhandler.py
new file mode 100644
index 000000000..f60a75bbc
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/core/crashhandler.py
@@ -0,0 +1,236 @@
+# encoding: utf-8
+"""sys.excepthook for IPython itself, leaves a detailed report on disk.
+
+Authors:
+
+* Fernando Perez
+* Brian E. Granger
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (C) 2001-2007 Fernando Perez.
+# Copyright (C) 2008-2011 The IPython Development Team
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+
+import sys
+import traceback
+from pprint import pformat
+from pathlib import Path
+
+from IPython.core import ultratb
+from IPython.core.release import author_email
+from IPython.utils.sysinfo import sys_info
+from IPython.utils.py3compat import input
+
+from IPython.core.release import __version__ as version
+
+from typing import Optional
+
+#-----------------------------------------------------------------------------
+# Code
+#-----------------------------------------------------------------------------
+
+# Template for the user message.
+_default_message_template = """\
+Oops, {app_name} crashed. We do our best to make it stable, but...
+
+A crash report was automatically generated with the following information:
+ - A verbatim copy of the crash traceback.
+ - A copy of your input history during this session.
+ - Data on your current {app_name} configuration.
+
+It was left in the file named:
+\t'{crash_report_fname}'
+If you can email this file to the developers, the information in it will help
+them in understanding and correcting the problem.
+
+You can mail it to: {contact_name} at {contact_email}
+with the subject '{app_name} Crash Report'.
+
+If you want to do it now, the following command will work (under Unix):
+mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname}
+
+In your email, please also include information about:
+- The operating system under which the crash happened: Linux, macOS, Windows,
+ other, and which exact version (for example: Ubuntu 16.04.3, macOS 10.13.2,
+ Windows 10 Pro), and whether it is 32-bit or 64-bit;
+- How {app_name} was installed: using pip or conda, from GitHub, as part of
+ a Docker container, or other, providing more detail if possible;
+- How to reproduce the crash: what exact sequence of instructions can one
+ input to get the same crash? Ideally, find a minimal yet complete sequence
+ of instructions that yields the crash.
+
+To ensure accurate tracking of this issue, please file a report about it at:
+{bug_tracker}
+"""
+
+_lite_message_template = """
+If you suspect this is an IPython {version} bug, please report it at:
+ https://github.com/ipython/ipython/issues
+or send an email to the mailing list at {email}
+
+You can print a more detailed traceback right now with "%tb", or use "%debug"
+to interactively debug it.
+
+Extra-detailed tracebacks for bug-reporting purposes can be enabled via:
+ {config}Application.verbose_crash=True
+"""
+
+
+class CrashHandler(object):
+ """Customizable crash handlers for IPython applications.
+
+ Instances of this class provide a :meth:`__call__` method which can be
+ used as a ``sys.excepthook``. The :meth:`__call__` signature is::
+
+ def __call__(self, etype, evalue, etb)
+ """
+
+ message_template = _default_message_template
+ section_sep = '\n\n'+'*'*75+'\n\n'
+
+ def __init__(
+ self,
+ app,
+ contact_name: Optional[str] = None,
+ contact_email: Optional[str] = None,
+ bug_tracker: Optional[str] = None,
+ show_crash_traceback: bool = True,
+ call_pdb: bool = False,
+ ):
+ """Create a new crash handler
+
+ Parameters
+ ----------
+ app : Application
+ A running :class:`Application` instance, which will be queried at
+ crash time for internal information.
+ contact_name : str
+ A string with the name of the person to contact.
+ contact_email : str
+ A string with the email address of the contact.
+ bug_tracker : str
+ A string with the URL for your project's bug tracker.
+ show_crash_traceback : bool
+ If false, don't print the crash traceback on stderr, only generate
+ the on-disk report
+ call_pdb
+ Whether to call pdb on crash
+
+ Attributes
+ ----------
+ These instances contain some non-argument attributes which allow for
+ further customization of the crash handler's behavior. Please see the
+ source for further details.
+
+ """
+ self.crash_report_fname = "Crash_report_%s.txt" % app.name
+ self.app = app
+ self.call_pdb = call_pdb
+ #self.call_pdb = True # dbg
+ self.show_crash_traceback = show_crash_traceback
+ self.info = dict(app_name = app.name,
+ contact_name = contact_name,
+ contact_email = contact_email,
+ bug_tracker = bug_tracker,
+ crash_report_fname = self.crash_report_fname)
+
+
+ def __call__(self, etype, evalue, etb):
+ """Handle an exception, call for compatible with sys.excepthook"""
+
+ # do not allow the crash handler to be called twice without reinstalling it
+ # this prevents unlikely errors in the crash handling from entering an
+ # infinite loop.
+ sys.excepthook = sys.__excepthook__
+
+ # Report tracebacks shouldn't use color in general (safer for users)
+ color_scheme = 'NoColor'
+
+ # Use this ONLY for developer debugging (keep commented out for release)
+ #color_scheme = 'Linux' # dbg
+ try:
+ rptdir = self.app.ipython_dir
+ except:
+ rptdir = Path.cwd()
+ if rptdir is None or not Path.is_dir(rptdir):
+ rptdir = Path.cwd()
+ report_name = rptdir / self.crash_report_fname
+ # write the report filename into the instance dict so it can get
+ # properly expanded out in the user message template
+ self.crash_report_fname = report_name
+ self.info['crash_report_fname'] = report_name
+ TBhandler = ultratb.VerboseTB(
+ color_scheme=color_scheme,
+ long_header=1,
+ call_pdb=self.call_pdb,
+ )
+ if self.call_pdb:
+ TBhandler(etype,evalue,etb)
+ return
+ else:
+ traceback = TBhandler.text(etype,evalue,etb,context=31)
+
+ # print traceback to screen
+ if self.show_crash_traceback:
+ print(traceback, file=sys.stderr)
+
+ # and generate a complete report on disk
+ try:
+ report = open(report_name, "w", encoding="utf-8")
+ except:
+ print('Could not create crash report on disk.', file=sys.stderr)
+ return
+
+ with report:
+ # Inform user on stderr of what happened
+ print('\n'+'*'*70+'\n', file=sys.stderr)
+ print(self.message_template.format(**self.info), file=sys.stderr)
+
+ # Construct report on disk
+ report.write(self.make_report(traceback))
+
+ input("Hit to quit (your terminal may close):")
+
+ def make_report(self,traceback):
+ """Return a string containing a crash report."""
+
+ sec_sep = self.section_sep
+
+ report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
+ rpt_add = report.append
+ rpt_add(sys_info())
+
+ try:
+ config = pformat(self.app.config)
+ rpt_add(sec_sep)
+ rpt_add('Application name: %s\n\n' % self.app_name)
+ rpt_add('Current user configuration structure:\n\n')
+ rpt_add(config)
+ except:
+ pass
+ rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
+
+ return ''.join(report)
+
+
+def crash_handler_lite(etype, evalue, tb):
+ """a light excepthook, adding a small message to the usual traceback"""
+ traceback.print_exception(etype, evalue, tb)
+
+ from IPython.core.interactiveshell import InteractiveShell
+ if InteractiveShell.initialized():
+ # we are in a Shell environment, give %magic example
+ config = "%config "
+ else:
+ # we are not in a shell, show generic config
+ config = "c."
+ print(_lite_message_template.format(email=author_email, config=config, version=version), file=sys.stderr)
+
diff --git a/venv/lib/python3.10/site-packages/IPython/core/debugger.py b/venv/lib/python3.10/site-packages/IPython/core/debugger.py
new file mode 100644
index 000000000..73b032874
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/core/debugger.py
@@ -0,0 +1,998 @@
+# -*- coding: utf-8 -*-
+"""
+Pdb debugger class.
+
+
+This is an extension to PDB which adds a number of new features.
+Note that there is also the `IPython.terminal.debugger` class which provides UI
+improvements.
+
+We also strongly recommend to use this via the `ipdb` package, which provides
+extra configuration options.
+
+Among other things, this subclass of PDB:
+ - supports many IPython magics like pdef/psource
+ - hide frames in tracebacks based on `__tracebackhide__`
+ - allows to skip frames based on `__debuggerskip__`
+
+The skipping and hiding frames are configurable via the `skip_predicates`
+command.
+
+By default, frames from readonly files will be hidden, frames containing
+``__tracebackhide__=True`` will be hidden.
+
+Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
+frames value of ``__debuggerskip__`` is ``True`` will be skipped.
+
+ >>> def helpers_helper():
+ ... pass
+ ...
+ ... def helper_1():
+ ... print("don't step in me")
+ ... helpers_helpers() # will be stepped over unless breakpoint set.
+ ...
+ ...
+ ... def helper_2():
+ ... print("in me neither")
+ ...
+
+One can define a decorator that wraps a function between the two helpers:
+
+ >>> def pdb_skipped_decorator(function):
+ ...
+ ...
+ ... def wrapped_fn(*args, **kwargs):
+ ... __debuggerskip__ = True
+ ... helper_1()
+ ... __debuggerskip__ = False
+ ... result = function(*args, **kwargs)
+ ... __debuggerskip__ = True
+ ... helper_2()
+ ... # setting __debuggerskip__ to False again is not necessary
+ ... return result
+ ...
+ ... return wrapped_fn
+
+When decorating a function, ipdb will directly step into ``bar()`` by
+default:
+
+ >>> @foo_decorator
+ ... def bar(x, y):
+ ... return x * y
+
+
+You can toggle the behavior with
+
+ ipdb> skip_predicates debuggerskip false
+
+or configure it in your ``.pdbrc``
+
+
+
+License
+-------
+
+Modified from the standard pdb.Pdb class to avoid including readline, so that
+the command line completion of other programs which include this isn't
+damaged.
+
+In the future, this class will be expanded with improvements over the standard
+pdb.
+
+The original code in this file is mainly lifted out of cmd.py in Python 2.2,
+with minor changes. Licensing should therefore be under the standard Python
+terms. For details on the PSF (Python Software Foundation) standard license,
+see:
+
+https://docs.python.org/2/license.html
+
+
+All the changes since then are under the same license as IPython.
+
+"""
+
+#*****************************************************************************
+#
+# This file is licensed under the PSF license.
+#
+# Copyright (C) 2001 Python Software Foundation, www.python.org
+# Copyright (C) 2005-2006 Fernando Perez.
+#
+#
+#*****************************************************************************
+
+import inspect
+import linecache
+import sys
+import re
+import os
+
+from IPython import get_ipython
+from IPython.utils import PyColorize
+from IPython.utils import coloransi, py3compat
+from IPython.core.excolors import exception_colors
+
+# skip module docstests
+__skip_doctest__ = True
+
+prompt = 'ipdb> '
+
+# We have to check this directly from sys.argv, config struct not yet available
+from pdb import Pdb as OldPdb
+
+# Allow the set_trace code to operate outside of an ipython instance, even if
+# it does so with some limitations. The rest of this support is implemented in
+# the Tracer constructor.
+
+DEBUGGERSKIP = "__debuggerskip__"
+
+
+def make_arrow(pad):
+ """generate the leading arrow in front of traceback or debugger"""
+ if pad >= 2:
+ return '-'*(pad-2) + '> '
+ elif pad == 1:
+ return '>'
+ return ''
+
+
+def BdbQuit_excepthook(et, ev, tb, excepthook=None):
+ """Exception hook which handles `BdbQuit` exceptions.
+
+ All other exceptions are processed using the `excepthook`
+ parameter.
+ """
+ raise ValueError(
+ "`BdbQuit_excepthook` is deprecated since version 5.1",
+ )
+
+
+def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
+ raise ValueError(
+ "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
+ DeprecationWarning, stacklevel=2)
+
+
+RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
+
+
+def strip_indentation(multiline_string):
+ return RGX_EXTRA_INDENT.sub('', multiline_string)
+
+
+def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
+ """Make new_fn have old_fn's doc string. This is particularly useful
+ for the ``do_...`` commands that hook into the help system.
+ Adapted from from a comp.lang.python posting
+ by Duncan Booth."""
+ def wrapper(*args, **kw):
+ return new_fn(*args, **kw)
+ if old_fn.__doc__:
+ wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
+ return wrapper
+
+
+class Pdb(OldPdb):
+ """Modified Pdb class, does not load readline.
+
+ for a standalone version that uses prompt_toolkit, see
+ `IPython.terminal.debugger.TerminalPdb` and
+ `IPython.terminal.debugger.set_trace()`
+
+
+ This debugger can hide and skip frames that are tagged according to some predicates.
+ See the `skip_predicates` commands.
+
+ """
+
+ default_predicates = {
+ "tbhide": True,
+ "readonly": False,
+ "ipython_internal": True,
+ "debuggerskip": True,
+ }
+
+ def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwargs):
+ """Create a new IPython debugger.
+
+ Parameters
+ ----------
+ completekey : default None
+ Passed to pdb.Pdb.
+ stdin : default None
+ Passed to pdb.Pdb.
+ stdout : default None
+ Passed to pdb.Pdb.
+ context : int
+ Number of lines of source code context to show when
+ displaying stacktrace information.
+ **kwargs
+ Passed to pdb.Pdb.
+
+ Notes
+ -----
+ The possibilities are python version dependent, see the python
+ docs for more info.
+ """
+
+ # Parent constructor:
+ try:
+ self.context = int(context)
+ if self.context <= 0:
+ raise ValueError("Context must be a positive integer")
+ except (TypeError, ValueError) as e:
+ raise ValueError("Context must be a positive integer") from e
+
+ # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
+ OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
+
+ # IPython changes...
+ self.shell = get_ipython()
+
+ if self.shell is None:
+ save_main = sys.modules['__main__']
+ # No IPython instance running, we must create one
+ from IPython.terminal.interactiveshell import \
+ TerminalInteractiveShell
+ self.shell = TerminalInteractiveShell.instance()
+ # needed by any code which calls __import__("__main__") after
+ # the debugger was entered. See also #9941.
+ sys.modules["__main__"] = save_main
+
+
+ color_scheme = self.shell.colors
+
+ self.aliases = {}
+
+ # Create color table: we copy the default one from the traceback
+ # module and add a few attributes needed for debugging
+ self.color_scheme_table = exception_colors()
+
+ # shorthands
+ C = coloransi.TermColors
+ cst = self.color_scheme_table
+
+ cst['NoColor'].colors.prompt = C.NoColor
+ cst['NoColor'].colors.breakpoint_enabled = C.NoColor
+ cst['NoColor'].colors.breakpoint_disabled = C.NoColor
+
+ cst['Linux'].colors.prompt = C.Green
+ cst['Linux'].colors.breakpoint_enabled = C.LightRed
+ cst['Linux'].colors.breakpoint_disabled = C.Red
+
+ cst['LightBG'].colors.prompt = C.Blue
+ cst['LightBG'].colors.breakpoint_enabled = C.LightRed
+ cst['LightBG'].colors.breakpoint_disabled = C.Red
+
+ cst['Neutral'].colors.prompt = C.Blue
+ cst['Neutral'].colors.breakpoint_enabled = C.LightRed
+ cst['Neutral'].colors.breakpoint_disabled = C.Red
+
+ # Add a python parser so we can syntax highlight source while
+ # debugging.
+ self.parser = PyColorize.Parser(style=color_scheme)
+ self.set_colors(color_scheme)
+
+ # Set the prompt - the default prompt is '(Pdb)'
+ self.prompt = prompt
+ self.skip_hidden = True
+ self.report_skipped = True
+
+ # list of predicates we use to skip frames
+ self._predicates = self.default_predicates
+
+ #
+ def set_colors(self, scheme):
+ """Shorthand access to the color table scheme selector method."""
+ self.color_scheme_table.set_active_scheme(scheme)
+ self.parser.style = scheme
+
+ def set_trace(self, frame=None):
+ if frame is None:
+ frame = sys._getframe().f_back
+ self.initial_frame = frame
+ return super().set_trace(frame)
+
+ def _hidden_predicate(self, frame):
+ """
+ Given a frame return whether it it should be hidden or not by IPython.
+ """
+
+ if self._predicates["readonly"]:
+ fname = frame.f_code.co_filename
+ # we need to check for file existence and interactively define
+ # function would otherwise appear as RO.
+ if os.path.isfile(fname) and not os.access(fname, os.W_OK):
+ return True
+
+ if self._predicates["tbhide"]:
+ if frame in (self.curframe, getattr(self, "initial_frame", None)):
+ return False
+ frame_locals = self._get_frame_locals(frame)
+ if "__tracebackhide__" not in frame_locals:
+ return False
+ return frame_locals["__tracebackhide__"]
+ return False
+
+ def hidden_frames(self, stack):
+ """
+ Given an index in the stack return whether it should be skipped.
+
+ This is used in up/down and where to skip frames.
+ """
+ # The f_locals dictionary is updated from the actual frame
+ # locals whenever the .f_locals accessor is called, so we
+ # avoid calling it here to preserve self.curframe_locals.
+ # Furthermore, there is no good reason to hide the current frame.
+ ip_hide = [self._hidden_predicate(s[0]) for s in stack]
+ ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
+ if ip_start and self._predicates["ipython_internal"]:
+ ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
+ return ip_hide
+
+ def interaction(self, frame, traceback):
+ try:
+ OldPdb.interaction(self, frame, traceback)
+ except KeyboardInterrupt:
+ self.stdout.write("\n" + self.shell.get_exception_only())
+
+ def precmd(self, line):
+ """Perform useful escapes on the command before it is executed."""
+
+ if line.endswith("??"):
+ line = "pinfo2 " + line[:-2]
+ elif line.endswith("?"):
+ line = "pinfo " + line[:-1]
+
+ line = super().precmd(line)
+
+ return line
+
+ def new_do_frame(self, arg):
+ OldPdb.do_frame(self, arg)
+
+ def new_do_quit(self, arg):
+
+ if hasattr(self, 'old_all_completions'):
+ self.shell.Completer.all_completions = self.old_all_completions
+
+ return OldPdb.do_quit(self, arg)
+
+ do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
+
+ def new_do_restart(self, arg):
+ """Restart command. In the context of ipython this is exactly the same
+ thing as 'quit'."""
+ self.msg("Restart doesn't make sense here. Using 'quit' instead.")
+ return self.do_quit(arg)
+
+ def print_stack_trace(self, context=None):
+ Colors = self.color_scheme_table.active_colors
+ ColorsNormal = Colors.Normal
+ if context is None:
+ context = self.context
+ try:
+ context = int(context)
+ if context <= 0:
+ raise ValueError("Context must be a positive integer")
+ except (TypeError, ValueError) as e:
+ raise ValueError("Context must be a positive integer") from e
+ try:
+ skipped = 0
+ for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
+ if hidden and self.skip_hidden:
+ skipped += 1
+ continue
+ if skipped:
+ print(
+ f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
+ )
+ skipped = 0
+ self.print_stack_entry(frame_lineno, context=context)
+ if skipped:
+ print(
+ f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
+ )
+ except KeyboardInterrupt:
+ pass
+
+ def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
+ context=None):
+ if context is None:
+ context = self.context
+ try:
+ context = int(context)
+ if context <= 0:
+ raise ValueError("Context must be a positive integer")
+ except (TypeError, ValueError) as e:
+ raise ValueError("Context must be a positive integer") from e
+ print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
+
+ # vds: >>
+ frame, lineno = frame_lineno
+ filename = frame.f_code.co_filename
+ self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
+ # vds: <<
+
+ def _get_frame_locals(self, frame):
+ """ "
+ Accessing f_local of current frame reset the namespace, so we want to avoid
+ that or the following can happen
+
+ ipdb> foo
+ "old"
+ ipdb> foo = "new"
+ ipdb> foo
+ "new"
+ ipdb> where
+ ipdb> foo
+ "old"
+
+ So if frame is self.current_frame we instead return self.curframe_locals
+
+ """
+ if frame is self.curframe:
+ return self.curframe_locals
+ else:
+ return frame.f_locals
+
+ def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
+ if context is None:
+ context = self.context
+ try:
+ context = int(context)
+ if context <= 0:
+ print("Context must be a positive integer", file=self.stdout)
+ except (TypeError, ValueError):
+ print("Context must be a positive integer", file=self.stdout)
+
+ import reprlib
+
+ ret = []
+
+ Colors = self.color_scheme_table.active_colors
+ ColorsNormal = Colors.Normal
+ tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
+ tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
+ tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
+ tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
+
+ frame, lineno = frame_lineno
+
+ return_value = ''
+ loc_frame = self._get_frame_locals(frame)
+ if "__return__" in loc_frame:
+ rv = loc_frame["__return__"]
+ # return_value += '->'
+ return_value += reprlib.repr(rv) + "\n"
+ ret.append(return_value)
+
+ #s = filename + '(' + `lineno` + ')'
+ filename = self.canonic(frame.f_code.co_filename)
+ link = tpl_link % py3compat.cast_unicode(filename)
+
+ if frame.f_code.co_name:
+ func = frame.f_code.co_name
+ else:
+ func = ""
+
+ call = ""
+ if func != "?":
+ if "__args__" in loc_frame:
+ args = reprlib.repr(loc_frame["__args__"])
+ else:
+ args = '()'
+ call = tpl_call % (func, args)
+
+ # The level info should be generated in the same format pdb uses, to
+ # avoid breaking the pdbtrack functionality of python-mode in *emacs.
+ if frame is self.curframe:
+ ret.append('> ')
+ else:
+ ret.append(" ")
+ ret.append("%s(%s)%s\n" % (link, lineno, call))
+
+ start = lineno - 1 - context//2
+ lines = linecache.getlines(filename)
+ start = min(start, len(lines) - context)
+ start = max(start, 0)
+ lines = lines[start : start + context]
+
+ for i, line in enumerate(lines):
+ show_arrow = start + 1 + i == lineno
+ linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
+ ret.append(
+ self.__format_line(
+ linetpl, filename, start + 1 + i, line, arrow=show_arrow
+ )
+ )
+ return "".join(ret)
+
+ def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
+ bp_mark = ""
+ bp_mark_color = ""
+
+ new_line, err = self.parser.format2(line, 'str')
+ if not err:
+ line = new_line
+
+ bp = None
+ if lineno in self.get_file_breaks(filename):
+ bps = self.get_breaks(filename, lineno)
+ bp = bps[-1]
+
+ if bp:
+ Colors = self.color_scheme_table.active_colors
+ bp_mark = str(bp.number)
+ bp_mark_color = Colors.breakpoint_enabled
+ if not bp.enabled:
+ bp_mark_color = Colors.breakpoint_disabled
+
+ numbers_width = 7
+ if arrow:
+ # This is the line with the error
+ pad = numbers_width - len(str(lineno)) - len(bp_mark)
+ num = '%s%s' % (make_arrow(pad), str(lineno))
+ else:
+ num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
+
+ return tpl_line % (bp_mark_color + bp_mark, num, line)
+
+ def print_list_lines(self, filename, first, last):
+ """The printing (as opposed to the parsing part of a 'list'
+ command."""
+ try:
+ Colors = self.color_scheme_table.active_colors
+ ColorsNormal = Colors.Normal
+ tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
+ tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
+ src = []
+ if filename == "" and hasattr(self, "_exec_filename"):
+ filename = self._exec_filename
+
+ for lineno in range(first, last+1):
+ line = linecache.getline(filename, lineno)
+ if not line:
+ break
+
+ if lineno == self.curframe.f_lineno:
+ line = self.__format_line(
+ tpl_line_em, filename, lineno, line, arrow=True
+ )
+ else:
+ line = self.__format_line(
+ tpl_line, filename, lineno, line, arrow=False
+ )
+
+ src.append(line)
+ self.lineno = lineno
+
+ print(''.join(src), file=self.stdout)
+
+ except KeyboardInterrupt:
+ pass
+
+ def do_skip_predicates(self, args):
+ """
+ Turn on/off individual predicates as to whether a frame should be hidden/skip.
+
+ The global option to skip (or not) hidden frames is set with skip_hidden
+
+ To change the value of a predicate
+
+ skip_predicates key [true|false]
+
+ Call without arguments to see the current values.
+
+ To permanently change the value of an option add the corresponding
+ command to your ``~/.pdbrc`` file. If you are programmatically using the
+ Pdb instance you can also change the ``default_predicates`` class
+ attribute.
+ """
+ if not args.strip():
+ print("current predicates:")
+ for (p, v) in self._predicates.items():
+ print(" ", p, ":", v)
+ return
+ type_value = args.strip().split(" ")
+ if len(type_value) != 2:
+ print(
+ f"Usage: skip_predicates , with one of {set(self._predicates.keys())}"
+ )
+ return
+
+ type_, value = type_value
+ if type_ not in self._predicates:
+ print(f"{type_!r} not in {set(self._predicates.keys())}")
+ return
+ if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
+ print(
+ f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
+ )
+ return
+
+ self._predicates[type_] = value.lower() in ("true", "yes", "1")
+ if not any(self._predicates.values()):
+ print(
+ "Warning, all predicates set to False, skip_hidden may not have any effects."
+ )
+
+ def do_skip_hidden(self, arg):
+ """
+ Change whether or not we should skip frames with the
+ __tracebackhide__ attribute.
+ """
+ if not arg.strip():
+ print(
+ f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
+ )
+ elif arg.strip().lower() in ("true", "yes"):
+ self.skip_hidden = True
+ elif arg.strip().lower() in ("false", "no"):
+ self.skip_hidden = False
+ if not any(self._predicates.values()):
+ print(
+ "Warning, all predicates set to False, skip_hidden may not have any effects."
+ )
+
+ def do_list(self, arg):
+ """Print lines of code from the current stack frame
+ """
+ self.lastcmd = 'list'
+ last = None
+ if arg:
+ try:
+ x = eval(arg, {}, {})
+ if type(x) == type(()):
+ first, last = x
+ first = int(first)
+ last = int(last)
+ if last < first:
+ # Assume it's a count
+ last = first + last
+ else:
+ first = max(1, int(x) - 5)
+ except:
+ print('*** Error in argument:', repr(arg), file=self.stdout)
+ return
+ elif self.lineno is None:
+ first = max(1, self.curframe.f_lineno - 5)
+ else:
+ first = self.lineno + 1
+ if last is None:
+ last = first + 10
+ self.print_list_lines(self.curframe.f_code.co_filename, first, last)
+
+ # vds: >>
+ lineno = first
+ filename = self.curframe.f_code.co_filename
+ self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
+ # vds: <<
+
+ do_l = do_list
+
+ def getsourcelines(self, obj):
+ lines, lineno = inspect.findsource(obj)
+ if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
+ # must be a module frame: do not try to cut a block out of it
+ return lines, 1
+ elif inspect.ismodule(obj):
+ return lines, 1
+ return inspect.getblock(lines[lineno:]), lineno+1
+
+ def do_longlist(self, arg):
+ """Print lines of code from the current stack frame.
+
+ Shows more lines than 'list' does.
+ """
+ self.lastcmd = 'longlist'
+ try:
+ lines, lineno = self.getsourcelines(self.curframe)
+ except OSError as err:
+ self.error(err)
+ return
+ last = lineno + len(lines)
+ self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
+ do_ll = do_longlist
+
+ def do_debug(self, arg):
+ """debug code
+ Enter a recursive debugger that steps through the code
+ argument (which is an arbitrary expression or statement to be
+ executed in the current environment).
+ """
+ trace_function = sys.gettrace()
+ sys.settrace(None)
+ globals = self.curframe.f_globals
+ locals = self.curframe_locals
+ p = self.__class__(completekey=self.completekey,
+ stdin=self.stdin, stdout=self.stdout)
+ p.use_rawinput = self.use_rawinput
+ p.prompt = "(%s) " % self.prompt.strip()
+ self.message("ENTERING RECURSIVE DEBUGGER")
+ sys.call_tracing(p.run, (arg, globals, locals))
+ self.message("LEAVING RECURSIVE DEBUGGER")
+ sys.settrace(trace_function)
+ self.lastcmd = p.lastcmd
+
+ def do_pdef(self, arg):
+ """Print the call signature for any callable object.
+
+ The debugger interface to %pdef"""
+ namespaces = [
+ ("Locals", self.curframe_locals),
+ ("Globals", self.curframe.f_globals),
+ ]
+ self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
+
+ def do_pdoc(self, arg):
+ """Print the docstring for an object.
+
+ The debugger interface to %pdoc."""
+ namespaces = [
+ ("Locals", self.curframe_locals),
+ ("Globals", self.curframe.f_globals),
+ ]
+ self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
+
+ def do_pfile(self, arg):
+ """Print (or run through pager) the file where an object is defined.
+
+ The debugger interface to %pfile.
+ """
+ namespaces = [
+ ("Locals", self.curframe_locals),
+ ("Globals", self.curframe.f_globals),
+ ]
+ self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
+
+ def do_pinfo(self, arg):
+ """Provide detailed information about an object.
+
+ The debugger interface to %pinfo, i.e., obj?."""
+ namespaces = [
+ ("Locals", self.curframe_locals),
+ ("Globals", self.curframe.f_globals),
+ ]
+ self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
+
+ def do_pinfo2(self, arg):
+ """Provide extra detailed information about an object.
+
+ The debugger interface to %pinfo2, i.e., obj??."""
+ namespaces = [
+ ("Locals", self.curframe_locals),
+ ("Globals", self.curframe.f_globals),
+ ]
+ self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
+
+ def do_psource(self, arg):
+ """Print (or run through pager) the source code for an object."""
+ namespaces = [
+ ("Locals", self.curframe_locals),
+ ("Globals", self.curframe.f_globals),
+ ]
+ self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
+
+ def do_where(self, arg):
+ """w(here)
+ Print a stack trace, with the most recent frame at the bottom.
+ An arrow indicates the "current frame", which determines the
+ context of most commands. 'bt' is an alias for this command.
+
+ Take a number as argument as an (optional) number of context line to
+ print"""
+ if arg:
+ try:
+ context = int(arg)
+ except ValueError as err:
+ self.error(err)
+ return
+ self.print_stack_trace(context)
+ else:
+ self.print_stack_trace()
+
+ do_w = do_where
+
+ def break_anywhere(self, frame):
+ """
+ _stop_in_decorator_internals is overly restrictive, as we may still want
+ to trace function calls, so we need to also update break_anywhere so
+ that is we don't `stop_here`, because of debugger skip, we may still
+ stop at any point inside the function
+
+ """
+
+ sup = super().break_anywhere(frame)
+ if sup:
+ return sup
+ if self._predicates["debuggerskip"]:
+ if DEBUGGERSKIP in frame.f_code.co_varnames:
+ return True
+ if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
+ return True
+ return False
+
+ def _is_in_decorator_internal_and_should_skip(self, frame):
+ """
+ Utility to tell us whether we are in a decorator internal and should stop.
+
+ """
+
+ # if we are disabled don't skip
+ if not self._predicates["debuggerskip"]:
+ return False
+
+ # if frame is tagged, skip by default.
+ if DEBUGGERSKIP in frame.f_code.co_varnames:
+ return True
+
+ # if one of the parent frame value set to True skip as well.
+
+ cframe = frame
+ while getattr(cframe, "f_back", None):
+ cframe = cframe.f_back
+ if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
+ return True
+
+ return False
+
+ def stop_here(self, frame):
+
+ if self._is_in_decorator_internal_and_should_skip(frame) is True:
+ return False
+
+ hidden = False
+ if self.skip_hidden:
+ hidden = self._hidden_predicate(frame)
+ if hidden:
+ if self.report_skipped:
+ Colors = self.color_scheme_table.active_colors
+ ColorsNormal = Colors.Normal
+ print(
+ f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
+ )
+ return super().stop_here(frame)
+
+ def do_up(self, arg):
+ """u(p) [count]
+ Move the current frame count (default one) levels up in the
+ stack trace (to an older frame).
+
+ Will skip hidden frames.
+ """
+ # modified version of upstream that skips
+ # frames with __tracebackhide__
+ if self.curindex == 0:
+ self.error("Oldest frame")
+ return
+ try:
+ count = int(arg or 1)
+ except ValueError:
+ self.error("Invalid frame count (%s)" % arg)
+ return
+ skipped = 0
+ if count < 0:
+ _newframe = 0
+ else:
+ counter = 0
+ hidden_frames = self.hidden_frames(self.stack)
+ for i in range(self.curindex - 1, -1, -1):
+ if hidden_frames[i] and self.skip_hidden:
+ skipped += 1
+ continue
+ counter += 1
+ if counter >= count:
+ break
+ else:
+ # if no break occurred.
+ self.error(
+ "all frames above hidden, use `skip_hidden False` to get get into those."
+ )
+ return
+
+ Colors = self.color_scheme_table.active_colors
+ ColorsNormal = Colors.Normal
+ _newframe = i
+ self._select_frame(_newframe)
+ if skipped:
+ print(
+ f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
+ )
+
+ def do_down(self, arg):
+ """d(own) [count]
+ Move the current frame count (default one) levels down in the
+ stack trace (to a newer frame).
+
+ Will skip hidden frames.
+ """
+ if self.curindex + 1 == len(self.stack):
+ self.error("Newest frame")
+ return
+ try:
+ count = int(arg or 1)
+ except ValueError:
+ self.error("Invalid frame count (%s)" % arg)
+ return
+ if count < 0:
+ _newframe = len(self.stack) - 1
+ else:
+ counter = 0
+ skipped = 0
+ hidden_frames = self.hidden_frames(self.stack)
+ for i in range(self.curindex + 1, len(self.stack)):
+ if hidden_frames[i] and self.skip_hidden:
+ skipped += 1
+ continue
+ counter += 1
+ if counter >= count:
+ break
+ else:
+ self.error(
+ "all frames below hidden, use `skip_hidden False` to get get into those."
+ )
+ return
+
+ Colors = self.color_scheme_table.active_colors
+ ColorsNormal = Colors.Normal
+ if skipped:
+ print(
+ f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
+ )
+ _newframe = i
+
+ self._select_frame(_newframe)
+
+ do_d = do_down
+ do_u = do_up
+
+ def do_context(self, context):
+ """context number_of_lines
+ Set the number of lines of source code to show when displaying
+ stacktrace information.
+ """
+ try:
+ new_context = int(context)
+ if new_context <= 0:
+ raise ValueError()
+ self.context = new_context
+ except ValueError:
+ self.error("The 'context' command requires a positive integer argument.")
+
+
+class InterruptiblePdb(Pdb):
+ """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
+
+ def cmdloop(self, intro=None):
+ """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
+ try:
+ return OldPdb.cmdloop(self, intro=intro)
+ except KeyboardInterrupt:
+ self.stop_here = lambda frame: False
+ self.do_quit("")
+ sys.settrace(None)
+ self.quitting = False
+ raise
+
+ def _cmdloop(self):
+ while True:
+ try:
+ # keyboard interrupts allow for an easy way to cancel
+ # the current command, so allow them during interactive input
+ self.allow_kbdint = True
+ self.cmdloop()
+ self.allow_kbdint = False
+ break
+ except KeyboardInterrupt:
+ self.message('--KeyboardInterrupt--')
+ raise
+
+
+def set_trace(frame=None):
+ """
+ Start debugging from `frame`.
+
+ If frame is not specified, debugging starts from caller's frame.
+ """
+ Pdb().set_trace(frame or sys._getframe().f_back)
diff --git a/venv/lib/python3.10/site-packages/IPython/core/display.py b/venv/lib/python3.10/site-packages/IPython/core/display.py
new file mode 100644
index 000000000..23d8636b5
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/IPython/core/display.py
@@ -0,0 +1,1290 @@
+# -*- coding: utf-8 -*-
+"""Top-level display functions for displaying object in different formats."""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+
+from binascii import b2a_base64, hexlify
+import html
+import json
+import mimetypes
+import os
+import struct
+import warnings
+from copy import deepcopy
+from os.path import splitext
+from pathlib import Path, PurePath
+
+from IPython.utils.py3compat import cast_unicode
+from IPython.testing.skipdoctest import skip_doctest
+from . import display_functions
+
+
+__all__ = ['display_pretty', 'display_html', 'display_markdown',
+ 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
+ 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
+ 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
+ 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
+ 'set_matplotlib_close',
+ 'Video']
+
+_deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
+
+__all__ = __all__ + _deprecated_names
+
+
+# ----- warn to import from IPython.display -----
+
+from warnings import warn
+
+
+def __getattr__(name):
+ if name in _deprecated_names:
+ warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2)
+ return getattr(display_functions, name)
+
+ if name in globals().keys():
+ return globals()[name]
+ else:
+ raise AttributeError(f"module {__name__} has no attribute {name}")
+
+
+#-----------------------------------------------------------------------------
+# utility functions
+#-----------------------------------------------------------------------------
+
+def _safe_exists(path):
+ """Check path, but don't let exceptions raise"""
+ try:
+ return os.path.exists(path)
+ except Exception:
+ return False
+
+
+def _display_mimetype(mimetype, objs, raw=False, metadata=None):
+ """internal implementation of all display_foo methods
+
+ Parameters
+ ----------
+ mimetype : str
+ The mimetype to be published (e.g. 'image/png')
+ *objs : object
+ The Python objects to display, or if raw=True raw text data to
+ display.
+ raw : bool
+ Are the data objects raw data or Python objects that need to be
+ formatted before display? [default: False]
+ metadata : dict (optional)
+ Metadata to be associated with the specific mimetype output.
+ """
+ if metadata:
+ metadata = {mimetype: metadata}
+ if raw:
+ # turn list of pngdata into list of { 'image/png': pngdata }
+ objs = [ {mimetype: obj} for obj in objs ]
+ display_functions.display(*objs, raw=raw, metadata=metadata, include=[mimetype])
+
+#-----------------------------------------------------------------------------
+# Main functions
+#-----------------------------------------------------------------------------
+
+
+def display_pretty(*objs, **kwargs):
+ """Display the pretty (default) representation of an object.
+
+ Parameters
+ ----------
+ *objs : object
+ The Python objects to display, or if raw=True raw text data to
+ display.
+ raw : bool
+ Are the data objects raw data or Python objects that need to be
+ formatted before display? [default: False]
+ metadata : dict (optional)
+ Metadata to be associated with the specific mimetype output.
+ """
+ _display_mimetype('text/plain', objs, **kwargs)
+
+
+def display_html(*objs, **kwargs):
+ """Display the HTML representation of an object.
+
+ Note: If raw=False and the object does not have a HTML
+ representation, no HTML will be shown.
+
+ Parameters
+ ----------
+ *objs : object
+ The Python objects to display, or if raw=True raw HTML data to
+ display.
+ raw : bool
+ Are the data objects raw data or Python objects that need to be
+ formatted before display? [default: False]
+ metadata : dict (optional)
+ Metadata to be associated with the specific mimetype output.
+ """
+ _display_mimetype('text/html', objs, **kwargs)
+
+
+def display_markdown(*objs, **kwargs):
+ """Displays the Markdown representation of an object.
+
+ Parameters
+ ----------
+ *objs : object
+ The Python objects to display, or if raw=True raw markdown data to
+ display.
+ raw : bool
+ Are the data objects raw data or Python objects that need to be
+ formatted before display? [default: False]
+ metadata : dict (optional)
+ Metadata to be associated with the specific mimetype output.
+ """
+
+ _display_mimetype('text/markdown', objs, **kwargs)
+
+
+def display_svg(*objs, **kwargs):
+ """Display the SVG representation of an object.
+
+ Parameters
+ ----------
+ *objs : object
+ The Python objects to display, or if raw=True raw svg data to
+ display.
+ raw : bool
+ Are the data objects raw data or Python objects that need to be
+ formatted before display? [default: False]
+ metadata : dict (optional)
+ Metadata to be associated with the specific mimetype output.
+ """
+ _display_mimetype('image/svg+xml', objs, **kwargs)
+
+
+def display_png(*objs, **kwargs):
+ """Display the PNG representation of an object.
+
+ Parameters
+ ----------
+ *objs : object
+ The Python objects to display, or if raw=True raw png data to
+ display.
+ raw : bool
+ Are the data objects raw data or Python objects that need to be
+ formatted before display? [default: False]
+ metadata : dict (optional)
+ Metadata to be associated with the specific mimetype output.
+ """
+ _display_mimetype('image/png', objs, **kwargs)
+
+
+def display_jpeg(*objs, **kwargs):
+ """Display the JPEG representation of an object.
+
+ Parameters
+ ----------
+ *objs : object
+ The Python objects to display, or if raw=True raw JPEG data to
+ display.
+ raw : bool
+ Are the data objects raw data or Python objects that need to be
+ formatted before display? [default: False]
+ metadata : dict (optional)
+ Metadata to be associated with the specific mimetype output.
+ """
+ _display_mimetype('image/jpeg', objs, **kwargs)
+
+
+def display_latex(*objs, **kwargs):
+ """Display the LaTeX representation of an object.
+
+ Parameters
+ ----------
+ *objs : object
+ The Python objects to display, or if raw=True raw latex data to
+ display.
+ raw : bool
+ Are the data objects raw data or Python objects that need to be
+ formatted before display? [default: False]
+ metadata : dict (optional)
+ Metadata to be associated with the specific mimetype output.
+ """
+ _display_mimetype('text/latex', objs, **kwargs)
+
+
+def display_json(*objs, **kwargs):
+ """Display the JSON representation of an object.
+
+ Note that not many frontends support displaying JSON.
+
+ Parameters
+ ----------
+ *objs : object
+ The Python objects to display, or if raw=True raw json data to
+ display.
+ raw : bool
+ Are the data objects raw data or Python objects that need to be
+ formatted before display? [default: False]
+ metadata : dict (optional)
+ Metadata to be associated with the specific mimetype output.
+ """
+ _display_mimetype('application/json', objs, **kwargs)
+
+
+def display_javascript(*objs, **kwargs):
+ """Display the Javascript representation of an object.
+
+ Parameters
+ ----------
+ *objs : object
+ The Python objects to display, or if raw=True raw javascript data to
+ display.
+ raw : bool
+ Are the data objects raw data or Python objects that need to be
+ formatted before display? [default: False]
+ metadata : dict (optional)
+ Metadata to be associated with the specific mimetype output.
+ """
+ _display_mimetype('application/javascript', objs, **kwargs)
+
+
+def display_pdf(*objs, **kwargs):
+ """Display the PDF representation of an object.
+
+ Parameters
+ ----------
+ *objs : object
+ The Python objects to display, or if raw=True raw javascript data to
+ display.
+ raw : bool
+ Are the data objects raw data or Python objects that need to be
+ formatted before display? [default: False]
+ metadata : dict (optional)
+ Metadata to be associated with the specific mimetype output.
+ """
+ _display_mimetype('application/pdf', objs, **kwargs)
+
+
+#-----------------------------------------------------------------------------
+# Smart classes
+#-----------------------------------------------------------------------------
+
+
+class DisplayObject(object):
+ """An object that wraps data to be displayed."""
+
+ _read_flags = 'r'
+ _show_mem_addr = False
+ metadata = None
+
+ def __init__(self, data=None, url=None, filename=None, metadata=None):
+ """Create a display object given raw data.
+
+ When this object is returned by an expression or passed to the
+ display function, it will result in the data being displayed
+ in the frontend. The MIME type of the data should match the
+ subclasses used, so the Png subclass should be used for 'image/png'
+ data. If the data is a URL, the data will first be downloaded
+ and then displayed. If
+
+ Parameters
+ ----------
+ data : unicode, str or bytes
+ The raw data or a URL or file to load the data from
+ url : unicode
+ A URL to download the data from.
+ filename : unicode
+ Path to a local file to load the data from.
+ metadata : dict
+ Dict of metadata associated to be the object when displayed
+ """
+ if isinstance(data, (Path, PurePath)):
+ data = str(data)
+
+ if data is not None and isinstance(data, str):
+ if data.startswith('http') and url is None:
+ url = data
+ filename = None
+ data = None
+ elif _safe_exists(data) and filename is None:
+ url = None
+ filename = data
+ data = None
+
+ self.url = url
+ self.filename = filename
+ # because of @data.setter methods in
+ # subclasses ensure url and filename are set
+ # before assigning to self.data
+ self.data = data
+
+ if metadata is not None:
+ self.metadata = metadata
+ elif self.metadata is None:
+ self.metadata = {}
+
+ self.reload()
+ self._check_data()
+
+ def __repr__(self):
+ if not self._show_mem_addr:
+ cls = self.__class__
+ r = "<%s.%s object>" % (cls.__module__, cls.__name__)
+ else:
+ r = super(DisplayObject, self).__repr__()
+ return r
+
+ def _check_data(self):
+ """Override in subclasses if there's something to check."""
+ pass
+
+ def _data_and_metadata(self):
+ """shortcut for returning metadata with shape information, if defined"""
+ if self.metadata:
+ return self.data, deepcopy(self.metadata)
+ else:
+ return self.data
+
+ def reload(self):
+ """Reload the raw data from file or URL."""
+ if self.filename is not None:
+ encoding = None if "b" in self._read_flags else "utf-8"
+ with open(self.filename, self._read_flags, encoding=encoding) as f:
+ self.data = f.read()
+ elif self.url is not None:
+ # Deferred import
+ from urllib.request import urlopen
+ response = urlopen(self.url)
+ data = response.read()
+ # extract encoding from header, if there is one:
+ encoding = None
+ if 'content-type' in response.headers:
+ for sub in response.headers['content-type'].split(';'):
+ sub = sub.strip()
+ if sub.startswith('charset'):
+ encoding = sub.split('=')[-1].strip()
+ break
+ if 'content-encoding' in response.headers:
+ # TODO: do deflate?
+ if 'gzip' in response.headers['content-encoding']:
+ import gzip
+ from io import BytesIO
+
+ # assume utf-8 if encoding is not specified
+ with gzip.open(
+ BytesIO(data), "rt", encoding=encoding or "utf-8"
+ ) as fp:
+ encoding = None
+ data = fp.read()
+
+ # decode data, if an encoding was specified
+ # We only touch self.data once since
+ # subclasses such as SVG have @data.setter methods
+ # that transform self.data into ... well svg.
+ if encoding:
+ self.data = data.decode(encoding, 'replace')
+ else:
+ self.data = data
+
+
+class TextDisplayObject(DisplayObject):
+ """Create a text display object given raw data.
+
+ Parameters
+ ----------
+ data : str or unicode
+ The raw data or a URL or file to load the data from.
+ url : unicode
+ A URL to download the data from.
+ filename : unicode
+ Path to a local file to load the data from.
+ metadata : dict
+ Dict of metadata associated to be the object when displayed
+ """
+ def _check_data(self):
+ if self.data is not None and not isinstance(self.data, str):
+ raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
+
+class Pretty(TextDisplayObject):
+
+ def _repr_pretty_(self, pp, cycle):
+ return pp.text(self.data)
+
+
+class HTML(TextDisplayObject):
+
+ def __init__(self, data=None, url=None, filename=None, metadata=None):
+ def warn():
+ if not data:
+ return False
+
+ #
+ # Avoid calling lower() on the entire data, because it could be a
+ # long string and we're only interested in its beginning and end.
+ #
+ prefix = data[:10].lower()
+ suffix = data[-10:].lower()
+ return prefix.startswith("
+
+
Stay Informed
+
Receive updates on new releases and upcoming projects.
diff --git a/venv/lib/python3.10/site-packages/docs/conf.py b/venv/lib/python3.10/site-packages/docs/conf.py
new file mode 100644
index 000000000..70f2b6fbd
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/docs/conf.py
@@ -0,0 +1,229 @@
+#
+# pipenv documentation build configuration file, created by
+# sphinx-quickstart on Mon Jan 30 13:28:36 2017.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+
+# Path hackery to get current version number.
+here = os.path.abspath(os.path.dirname(__file__))
+
+about = {}
+with open(os.path.join(here, "..", "pipenv", "__version__.py")) as f:
+ exec(f.read(), about)
+
+# Hackery to get the CLI docs to generate
+import click
+
+import pipenv.vendor.click
+
+click.Command = pipenv.vendor.click.Command
+click.Group = pipenv.vendor.click.Group
+click.BaseCommand = pipenv.vendor.click.BaseCommand
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ "sphinx.ext.autodoc",
+ "sphinx.ext.todo",
+ "sphinx.ext.coverage",
+ "sphinx.ext.viewcode",
+ "myst_parser",
+ "sphinx_click",
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ["_templates"]
+
+myst_enable_extensions = [
+ "amsmath",
+ "colon_fence",
+ "deflist",
+ "dollarmath",
+ "fieldlist",
+ "html_admonition",
+ "html_image",
+ "linkify",
+ "replacements",
+ "smartquotes",
+ "strikethrough",
+ "substitution",
+ "tasklist",
+]
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+source_suffix = [".rst", ".md"]
+# source_suffix = ".rst"
+
+# The master toctree document.
+master_doc = "index"
+
+# General information about the project.
+project = "pipenv"
+copyright = '2020. A project founded by Kenneth Reitz and maintained by Python Packaging Authority (PyPA).'
+author = "Python Packaging Authority"
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = about["__version__"]
+# The full version, including alpha/beta/rc tags.
+release = about["__version__"]
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = "sphinx"
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = True
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = "alabaster"
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#
+html_theme_options = {
+ "show_powered_by": False,
+ "github_user": "pypa",
+ "github_repo": "pipenv",
+ "github_banner": False,
+ "show_related": False,
+}
+
+html_sidebars = {
+ "index": ["sidebarlogo.html", "sourcelink.html", "searchbox.html", "hacks.html"],
+ "**": [
+ "sidebarlogo.html",
+ "localtoc.html",
+ "relations.html",
+ "sourcelink.html",
+ "searchbox.html",
+ "hacks.html",
+ ],
+}
+
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ["_static"]
+
+
+def setup(app):
+ app.add_css_file("custom.css")
+
+
+# -- Options for HTMLHelp output ------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = "pipenvdoc"
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, "pipenv.tex", "pipenv Documentation", "Kenneth Reitz", "manual"),
+]
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [("installation", "pipenv", "", [author], 1)]
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (
+ master_doc,
+ "pipenv",
+ "pipenv Documentation",
+ author,
+ "pipenv",
+ "One line description of project.",
+ "Miscellaneous",
+ ),
+]
+
+
+# -- Options for Epub output ----------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = project
+epub_author = author
+epub_publisher = author
+epub_copyright = copyright
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#
+# epub_identifier = ''
+
+# A unique identification for the text.
+#
+# epub_uid = ''
+
+# A list of files that should not be packed into the epub file.
+epub_exclude_files = ["search.html"]
diff --git a/venv/lib/python3.10/site-packages/docs/make.bat b/venv/lib/python3.10/site-packages/docs/make.bat
new file mode 100644
index 000000000..e8abb2042
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/docs/make.bat
@@ -0,0 +1,36 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+set SPHINXPROJ=pipenv
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+
+:end
+popd
diff --git a/venv/lib/python3.10/site-packages/examples/Pipfile b/venv/lib/python3.10/site-packages/examples/Pipfile
new file mode 100644
index 000000000..7c0d43468
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/examples/Pipfile
@@ -0,0 +1,11 @@
+[[source]]
+url = "https://pypi.python.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+requests = "*"
+
+
+[dev-packages]
+pytest = "*"
diff --git a/venv/lib/python3.10/site-packages/examples/Pipfile.lock b/venv/lib/python3.10/site-packages/examples/Pipfile.lock
new file mode 100644
index 000000000..659792494
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/examples/Pipfile.lock
@@ -0,0 +1,174 @@
+{
+ "_meta": {
+ "hash": {
+ "sha256": "8d14434df45e0ef884d6c3f6e8048ba72335637a8631cc44792f52fd20b6f97a"
+ },
+ "pipfile-spec": 6,
+ "requires": {},
+ "sources": [
+ {
+ "name": "pypi",
+ "url": "https://pypi.python.org/simple",
+ "verify_ssl": true
+ }
+ ]
+ },
+ "default": {
+ "certifi": {
+ "hashes": [
+ "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082",
+ "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==2023.7.22"
+ },
+ "charset-normalizer": {
+ "hashes": [
+ "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843",
+ "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786",
+ "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e",
+ "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8",
+ "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4",
+ "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa",
+ "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d",
+ "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82",
+ "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7",
+ "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895",
+ "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d",
+ "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a",
+ "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382",
+ "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678",
+ "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b",
+ "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e",
+ "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741",
+ "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4",
+ "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596",
+ "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9",
+ "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69",
+ "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c",
+ "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77",
+ "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13",
+ "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459",
+ "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e",
+ "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7",
+ "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908",
+ "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a",
+ "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f",
+ "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8",
+ "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482",
+ "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d",
+ "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d",
+ "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545",
+ "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34",
+ "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86",
+ "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6",
+ "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe",
+ "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e",
+ "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc",
+ "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7",
+ "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd",
+ "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c",
+ "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557",
+ "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a",
+ "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89",
+ "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078",
+ "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e",
+ "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4",
+ "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403",
+ "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0",
+ "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89",
+ "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115",
+ "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9",
+ "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05",
+ "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a",
+ "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec",
+ "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56",
+ "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38",
+ "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479",
+ "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c",
+ "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e",
+ "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd",
+ "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186",
+ "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455",
+ "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c",
+ "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65",
+ "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78",
+ "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287",
+ "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df",
+ "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43",
+ "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1",
+ "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7",
+ "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989",
+ "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a",
+ "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63",
+ "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884",
+ "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649",
+ "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810",
+ "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828",
+ "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4",
+ "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2",
+ "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd",
+ "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5",
+ "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe",
+ "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293",
+ "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e",
+ "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e",
+ "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8"
+ ],
+ "markers": "python_full_version >= '3.7.0'",
+ "version": "==3.3.0"
+ },
+ "idna": {
+ "hashes": [
+ "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
+ "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==3.4"
+ },
+ "requests": {
+ "hashes": [
+ "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f",
+ "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"
+ ],
+ "index": "pypi",
+ "markers": "python_version >= '3.7'",
+ "version": "==2.31.0"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84",
+ "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"
+ ],
+ "index": "pypi",
+ "markers": "python_version >= '3.7'",
+ "version": "==2.0.7"
+ }
+ },
+ "develop": {
+ "py": {
+ "hashes": [
+ "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
+ "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==1.11.0"
+ },
+ "pytest": {
+ "hashes": [
+ "sha256:b84f554f8ddc23add65c411bf112b2d88e2489fd45f753b1cae5936358bdf314",
+ "sha256:f46e49e0340a532764991c498244a60e3a37d7424a532b3ff1a6a7653f1a403a"
+ ],
+ "index": "pypi",
+ "version": "==3.2.2"
+ },
+ "setuptools": {
+ "hashes": [
+ "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87",
+ "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"
+ ],
+ "markers": "python_version >= '3.8'",
+ "version": "==68.2.2"
+ }
+ }
+}
diff --git a/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/INSTALLER
new file mode 100644
index 000000000..a1b589e38
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/LICENSE.txt b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/LICENSE.txt
new file mode 100644
index 000000000..473e36e24
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Alex Hall
+
+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/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/METADATA b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/METADATA
new file mode 100644
index 000000000..bf103f9c9
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/METADATA
@@ -0,0 +1,170 @@
+Metadata-Version: 2.1
+Name: executing
+Version: 1.2.0
+Summary: Get the currently executing AST node of a frame, and other information
+Home-page: https://github.com/alexmojaki/executing
+Author: Alex Hall
+Author-email: alex.mojaki@gmail.com
+License: MIT
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Description-Content-Type: text/markdown
+License-File: LICENSE.txt
+Requires-Dist: typing ; python_version < "3.5"
+Provides-Extra: tests
+Requires-Dist: asttokens ; extra == 'tests'
+Requires-Dist: pytest ; extra == 'tests'
+Requires-Dist: littleutils ; extra == 'tests'
+Requires-Dist: rich ; (python_version >= "3.11") and extra == 'tests'
+
+# executing
+
+[](https://github.com/alexmojaki/executing/actions) [](https://coveralls.io/github/alexmojaki/executing?branch=master) [](https://pypi.python.org/pypi/executing)
+
+This mini-package lets you get information about what a frame is currently doing, particularly the AST node being executed.
+
+* [Usage](#usage)
+ * [Getting the AST node](#getting-the-ast-node)
+ * [Getting the source code of the node](#getting-the-source-code-of-the-node)
+ * [Getting the `__qualname__` of the current function](#getting-the-__qualname__-of-the-current-function)
+ * [The Source class](#the-source-class)
+* [Installation](#installation)
+* [How does it work?](#how-does-it-work)
+* [Is it reliable?](#is-it-reliable)
+* [Which nodes can it identify?](#which-nodes-can-it-identify)
+* [Libraries that use this](#libraries-that-use-this)
+
+## Usage
+
+### Getting the AST node
+
+```python
+import executing
+
+node = executing.Source.executing(frame).node
+```
+
+Then `node` will be an AST node (from the `ast` standard library module) or None if the node couldn't be identified (which may happen often and should always be checked).
+
+`node` will always be the same instance for multiple calls with frames at the same point of execution.
+
+If you have a traceback object, pass it directly to `Source.executing()` rather than the `tb_frame` attribute to get the correct node.
+
+### Getting the source code of the node
+
+For this you will need to separately install the [`asttokens`](https://github.com/gristlabs/asttokens) library, then obtain an `ASTTokens` object:
+
+```python
+executing.Source.executing(frame).source.asttokens()
+```
+
+or:
+
+```python
+executing.Source.for_frame(frame).asttokens()
+```
+
+or use one of the convenience methods:
+
+```python
+executing.Source.executing(frame).text()
+executing.Source.executing(frame).text_range()
+```
+
+### Getting the `__qualname__` of the current function
+
+```python
+executing.Source.executing(frame).code_qualname()
+```
+
+or:
+
+```python
+executing.Source.for_frame(frame).code_qualname(frame.f_code)
+```
+
+### The `Source` class
+
+Everything goes through the `Source` class. Only one instance of the class is created for each filename. Subclassing it to add more attributes on creation or methods is recommended. The classmethods such as `executing` will respect this. See the source code and docstrings for more detail.
+
+## Installation
+
+ pip install executing
+
+If you don't like that you can just copy the file `executing.py`, there are no dependencies (but of course you won't get updates).
+
+## How does it work?
+
+Suppose the frame is executing this line:
+
+```python
+self.foo(bar.x)
+```
+
+and in particular it's currently obtaining the attribute `self.foo`. Looking at the bytecode, specifically `frame.f_code.co_code[frame.f_lasti]`, we can tell that it's loading an attribute, but it's not obvious which one. We can narrow down the statement being executed using `frame.f_lineno` and find the two `ast.Attribute` nodes representing `self.foo` and `bar.x`. How do we find out which one it is, without recreating the entire compiler in Python?
+
+The trick is to modify the AST slightly for each candidate expression and observe the changes in the bytecode instructions. We change the AST to this:
+
+```python
+(self.foo ** 'longuniqueconstant')(bar.x)
+```
+
+and compile it, and the bytecode will be almost the same but there will be two new instructions:
+
+ LOAD_CONST 'longuniqueconstant'
+ BINARY_POWER
+
+and just before that will be a `LOAD_ATTR` instruction corresponding to `self.foo`. Seeing that it's in the same position as the original instruction lets us know we've found our match.
+
+## Is it reliable?
+
+Yes - if it identifies a node, you can trust that it's identified the correct one. The tests are very thorough - in addition to unit tests which check various situations directly, there are property tests against a large number of files (see the filenames printed in [this build](https://travis-ci.org/alexmojaki/executing/jobs/557970457)) with real code. Specifically, for each file, the tests:
+
+ 1. Identify as many nodes as possible from all the bytecode instructions in the file, and assert that they are all distinct
+ 2. Find all the nodes that should be identifiable, and assert that they were indeed identified somewhere
+
+In other words, it shows that there is a one-to-one mapping between the nodes and the instructions that can be handled. This leaves very little room for a bug to creep in.
+
+Furthermore, `executing` checks that the instructions compiled from the modified AST exactly match the original code save for a few small known exceptions. This accounts for all the quirks and optimisations in the interpreter.
+
+## Which nodes can it identify?
+
+Currently it works in almost all cases for the following `ast` nodes:
+
+ - `Call`, e.g. `self.foo(bar)`
+ - `Attribute`, e.g. `point.x`
+ - `Subscript`, e.g. `lst[1]`
+ - `BinOp`, e.g. `x + y` (doesn't include `and` and `or`)
+ - `UnaryOp`, e.g. `-n` (includes `not` but only works sometimes)
+ - `Compare` e.g. `a < b` (not for chains such as `0 < p < 1`)
+
+The plan is to extend to more operations in the future.
+
+## Projects that use this
+
+### My Projects
+
+- **[`stack_data`](https://github.com/alexmojaki/stack_data)**: Extracts data from stack frames and tracebacks, particularly to display more useful tracebacks than the default. Also uses another related library of mine: **[`pure_eval`](https://github.com/alexmojaki/pure_eval)**.
+- **[`futurecoder`](https://futurecoder.io/)**: Highlights the executing node in tracebacks using `executing` via `stack_data`, and provides debugging with `snoop`.
+- **[`snoop`](https://github.com/alexmojaki/snoop)**: A feature-rich and convenient debugging library. Uses `executing` to show the operation which caused an exception and to allow the `pp` function to display the source of its arguments.
+- **[`heartrate`](https://github.com/alexmojaki/heartrate)**: A simple real time visualisation of the execution of a Python program. Uses `executing` to highlight currently executing operations, particularly in each frame of the stack trace.
+- **[`sorcery`](https://github.com/alexmojaki/sorcery)**: Dark magic delights in Python. Uses `executing` to let special callables called spells know where they're being called from.
+
+### Projects I've contributed to
+
+- **[`IPython`](https://github.com/ipython/ipython/pull/12150)**: Highlights the executing node in tracebacks using `executing` via [`stack_data`](https://github.com/alexmojaki/stack_data).
+- **[`icecream`](https://github.com/gruns/icecream)**: 🦠Sweet and creamy print debugging. Uses `executing` to identify where `ic` is called and print its arguments.
+- **[`friendly_traceback`](https://github.com/friendly-traceback/friendly-traceback)**: Uses `stack_data` and `executing` to pinpoint the cause of errors and provide helpful explanations.
+- **[`python-devtools`](https://github.com/samuelcolvin/python-devtools)**: Uses `executing` for print debugging similar to `icecream`.
+- **[`sentry_sdk`](https://github.com/getsentry/sentry-python)**: Add the integration `sentry_sdk.integrations.executingExecutingIntegration()` to show the function `__qualname__` in each frame in sentry events.
+- **[`varname`](https://github.com/pwwang/python-varname)**: Dark magics about variable names in python. Uses `executing` to find where its various magical functions like `varname` and `nameof` are called from.
diff --git a/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/RECORD b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/RECORD
new file mode 100644
index 000000000..c6348146d
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/RECORD
@@ -0,0 +1,18 @@
+executing-1.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+executing-1.2.0.dist-info/LICENSE.txt,sha256=pHaiyw70xBRQNApXeii5GsTH9mkTay7hSAR_q9X8QYE,1066
+executing-1.2.0.dist-info/METADATA,sha256=Vpt6k_A5v0Uft7TmzvFLpXvLNn7fBqySYmzYEh7TZ2c,8899
+executing-1.2.0.dist-info/RECORD,,
+executing-1.2.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+executing-1.2.0.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
+executing-1.2.0.dist-info/top_level.txt,sha256=b9Rtf3NtSqc0_Kak6L_lvnbdKPA0GUim2p-XcFQsf5g,10
+executing/__init__.py,sha256=Me_S5KWNafYIUlK5ybhA62yDPl4gWFNxA5ByDGfxzU4,758
+executing/__pycache__/__init__.cpython-310.pyc,,
+executing/__pycache__/_exceptions.cpython-310.pyc,,
+executing/__pycache__/_position_node_finder.cpython-310.pyc,,
+executing/__pycache__/executing.cpython-310.pyc,,
+executing/__pycache__/version.cpython-310.pyc,,
+executing/_exceptions.py,sha256=nf5P5jPnSjjo_8YWlh5AOyLZHF_hNyJpDv0OG2XFYgw,568
+executing/_position_node_finder.py,sha256=qGDoQtYS5pSij_I3gg9v-Xt1HU5hlamnjFtrkpUTAuI,19870
+executing/executing.py,sha256=M3s9rROVjVmhtL8UcWTp9cipGtVoFd-BkmSsA2TAfU8,45644
+executing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+executing/version.py,sha256=5GblYyMbk8JySosj59Rvi2uzLqfP-DAs77ikwTafXT4,21
diff --git a/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/REQUESTED b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/REQUESTED
new file mode 100644
index 000000000..e69de29bb
diff --git a/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/WHEEL b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/WHEEL
new file mode 100644
index 000000000..0b18a2811
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/top_level.txt
new file mode 100644
index 000000000..a920f2c56
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/executing-1.2.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+executing
diff --git a/venv/lib/python3.10/site-packages/executing/__init__.py b/venv/lib/python3.10/site-packages/executing/__init__.py
new file mode 100644
index 000000000..b64519739
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/executing/__init__.py
@@ -0,0 +1,25 @@
+"""
+Get information about what a frame is currently doing. Typical usage:
+
+ import executing
+
+ node = executing.Source.executing(frame).node
+ # node will be an AST node or None
+"""
+
+from collections import namedtuple
+_VersionInfo = namedtuple('_VersionInfo', ('major', 'minor', 'micro'))
+from .executing import Source, Executing, only, NotOneValueFound, cache, future_flags
+try:
+ from .version import __version__ # type: ignore[import]
+ if "dev" in __version__:
+ raise ValueError
+except Exception:
+ # version.py is auto-generated with the git tag when building
+ __version__ = "???"
+ __version_info__ = _VersionInfo(-1, -1, -1)
+else:
+ __version_info__ = _VersionInfo(*map(int, __version__.split('.')))
+
+
+__all__ = ["Source"]
diff --git a/venv/lib/python3.10/site-packages/executing/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/executing/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 000000000..49544124f
Binary files /dev/null and b/venv/lib/python3.10/site-packages/executing/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/executing/__pycache__/_exceptions.cpython-310.pyc b/venv/lib/python3.10/site-packages/executing/__pycache__/_exceptions.cpython-310.pyc
new file mode 100644
index 000000000..21818781d
Binary files /dev/null and b/venv/lib/python3.10/site-packages/executing/__pycache__/_exceptions.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/executing/__pycache__/_position_node_finder.cpython-310.pyc b/venv/lib/python3.10/site-packages/executing/__pycache__/_position_node_finder.cpython-310.pyc
new file mode 100644
index 000000000..6598f84aa
Binary files /dev/null and b/venv/lib/python3.10/site-packages/executing/__pycache__/_position_node_finder.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/executing/__pycache__/executing.cpython-310.pyc b/venv/lib/python3.10/site-packages/executing/__pycache__/executing.cpython-310.pyc
new file mode 100644
index 000000000..4dd51a98a
Binary files /dev/null and b/venv/lib/python3.10/site-packages/executing/__pycache__/executing.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/executing/__pycache__/version.cpython-310.pyc b/venv/lib/python3.10/site-packages/executing/__pycache__/version.cpython-310.pyc
new file mode 100644
index 000000000..889838fd4
Binary files /dev/null and b/venv/lib/python3.10/site-packages/executing/__pycache__/version.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/executing/_exceptions.py b/venv/lib/python3.10/site-packages/executing/_exceptions.py
new file mode 100644
index 000000000..1e88e3f29
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/executing/_exceptions.py
@@ -0,0 +1,22 @@
+
+class KnownIssue(Exception):
+ """
+ Raised in case of an known problem. Mostly because of cpython bugs.
+ Executing.node gets set to None in this case.
+ """
+
+ pass
+
+
+class VerifierFailure(Exception):
+ """
+ Thrown for an unexpected mapping from instruction to ast node
+ Executing.node gets set to None in this case.
+ """
+
+ def __init__(self, title, node, instruction):
+ # type: (object, object, object) -> None
+ self.node = node
+ self.instruction = instruction
+
+ super().__init__(title) # type: ignore[call-arg]
diff --git a/venv/lib/python3.10/site-packages/executing/_position_node_finder.py b/venv/lib/python3.10/site-packages/executing/_position_node_finder.py
new file mode 100644
index 000000000..8b3aec086
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/executing/_position_node_finder.py
@@ -0,0 +1,573 @@
+import ast
+import dis
+from types import CodeType, FrameType
+from typing import Any, Callable, Iterator, Optional, Sequence, Set, Tuple, Type, Union, cast
+from .executing import EnhancedAST, NotOneValueFound, Source, only, function_node_types, assert_
+from ._exceptions import KnownIssue, VerifierFailure
+
+from functools import lru_cache
+
+# the code in this module can use all python>=3.11 features
+
+
+def parents(node: EnhancedAST) -> Iterator[EnhancedAST]:
+ while True:
+ if hasattr(node, "parent"):
+ node = node.parent
+ yield node
+ else:
+ break
+
+def node_and_parents(node: EnhancedAST) -> Iterator[EnhancedAST]:
+ yield node
+ yield from parents(node)
+
+
+def mangled_name(node: EnhancedAST) -> str:
+ """
+
+ Parameters:
+ node: the node which should be mangled
+ name: the name of the node
+
+ Returns:
+ The mangled name of `node`
+ """
+ if isinstance(node, ast.Attribute):
+ name = node.attr
+ elif isinstance(node, ast.Name):
+ name = node.id
+ elif isinstance(node, (ast.alias)):
+ name = node.asname or node.name.split(".")[0]
+ elif isinstance(node, (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)):
+ name = node.name
+ elif isinstance(node, ast.ExceptHandler):
+ name = node.name or "exc"
+ else:
+ raise TypeError("no node to mangle")
+
+ if name.startswith("__") and not name.endswith("__"):
+
+ parent,child=node.parent,node
+
+ while not (isinstance(parent,ast.ClassDef) and child not in parent.bases):
+ if not hasattr(parent,"parent"):
+ break
+ parent,child=parent.parent,parent
+ else:
+ class_name=parent.name.lstrip("_")
+ if class_name!="":
+ return "_" + class_name + name
+
+
+
+ return name
+
+
+@lru_cache(128)
+def get_instructions(code: CodeType) -> list[dis.Instruction]:
+ return list(dis.get_instructions(code, show_caches=True))
+
+
+types_cmp_issue_fix = (
+ ast.IfExp,
+ ast.If,
+ ast.Assert,
+ ast.While,
+)
+
+types_cmp_issue = types_cmp_issue_fix + (
+ ast.ListComp,
+ ast.SetComp,
+ ast.DictComp,
+ ast.GeneratorExp,
+)
+
+op_type_map = {
+ "**": ast.Pow,
+ "*": ast.Mult,
+ "@": ast.MatMult,
+ "//": ast.FloorDiv,
+ "/": ast.Div,
+ "%": ast.Mod,
+ "+": ast.Add,
+ "-": ast.Sub,
+ "<<": ast.LShift,
+ ">>": ast.RShift,
+ "&": ast.BitAnd,
+ "^": ast.BitXor,
+ "|": ast.BitOr,
+}
+
+
+class PositionNodeFinder(object):
+ """
+ Mapping bytecode to ast-node based on the source positions, which where introduced in pyhon 3.11.
+ In general every ast-node can be exactly referenced by its begin/end line/col_offset, which is stored in the bytecode.
+ There are only some exceptions for methods and attributes.
+ """
+
+ def __init__(self, frame: FrameType, stmts: Set[EnhancedAST], tree: ast.Module, lasti: int, source: Source):
+ self.bc_list = get_instructions(frame.f_code)
+
+ self.source = source
+ self.decorator: Optional[EnhancedAST] = None
+
+ # work around for https://github.com/python/cpython/issues/96970
+ while self.opname(lasti) == "CACHE":
+ lasti -= 2
+
+ try:
+ # try to map with all match_positions
+ self.result = self.find_node(lasti)
+ except NotOneValueFound:
+ typ: tuple[Type]
+ # LOAD_METHOD could load "".join for long "..."%(...) BinOps
+ # this can only be associated by using all positions
+ if self.opname(lasti) in (
+ "LOAD_METHOD",
+ "LOAD_ATTR",
+ "STORE_ATTR",
+ "DELETE_ATTR",
+ ):
+ # lineno and col_offset of LOAD_METHOD and *_ATTR instructions get set to the beginning of
+ # the attribute by the python compiler to improved error messages (PEP-657)
+ # we ignore here the start position and try to find the ast-node just by end position and expected node type
+ # This is save, because there can only be one attribute ending at a specific point in the source code.
+ typ = (ast.Attribute,)
+ elif self.opname(lasti) == "CALL":
+ # A CALL instruction can be a method call, in which case the lineno and col_offset gets changed by the compiler.
+ # Therefore we ignoring here this attributes and searchnig for a Call-node only by end_col_offset and end_lineno.
+ # This is save, because there can only be one method ending at a specific point in the source code.
+ # One closing ) only belongs to one method.
+ typ = (ast.Call,)
+ else:
+ raise
+
+ self.result = self.find_node(
+ lasti,
+ match_positions=("end_col_offset", "end_lineno"),
+ typ=typ,
+ )
+
+ self.known_issues(self.result, self.instruction(lasti))
+
+ self.test_for_decorator(self.result, lasti)
+
+ if self.decorator is None:
+ self.verify(self.result, self.instruction(lasti))
+
+ def test_for_decorator(self, node: EnhancedAST, index: int) -> None:
+ if (
+ isinstance(node.parent, (ast.ClassDef, function_node_types))
+ and node in node.parent.decorator_list # type: ignore[attr-defined]
+ ):
+ node_func = node.parent
+
+ while True:
+ # the generated bytecode looks like follow:
+
+ # index opname
+ # ------------------
+ # index-4 PRECALL
+ # index-2 CACHE
+ # index CALL <- the call instruction
+ # ... CACHE some CACHE instructions
+
+ # maybe multiple other bytecode blocks for other decorators
+ # index-4 PRECALL
+ # index-2 CACHE
+ # index CALL <- index of the next loop
+ # ... CACHE some CACHE instructions
+
+ # index+x STORE_* the ast-node of this instruction points to the decorated thing
+
+ if self.opname(index - 4) != "PRECALL" or self.opname(index) != "CALL":
+ break
+
+ index += 2
+
+ while self.opname(index) in ("CACHE", "EXTENDED_ARG"):
+ index += 2
+
+ if (
+ self.opname(index).startswith("STORE_")
+ and self.find_node(index) == node_func
+ ):
+ self.result = node_func
+ self.decorator = node
+ return
+
+ index += 4
+
+ def known_issues(self, node: EnhancedAST, instruction: dis.Instruction) -> None:
+ if instruction.opname in ("COMPARE_OP", "IS_OP", "CONTAINS_OP") and isinstance(
+ node, types_cmp_issue
+ ):
+ if isinstance(node, types_cmp_issue_fix):
+ # this is a workaround for https://github.com/python/cpython/issues/95921
+ # we can fix cases with only on comparison inside the test condition
+ #
+ # we can not fix cases like:
+ # if a 1
+ ]
+
+ assert_(comparisons, "expected at least one comparison")
+
+ if len(comparisons) == 1:
+ node = self.result = cast(EnhancedAST, comparisons[0])
+ else:
+ raise KnownIssue(
+ "multiple chain comparison inside %s can not be fixed" % (node)
+ )
+
+ else:
+ # Comprehension and generators get not fixed for now.
+ raise KnownIssue("chain comparison inside %s can not be fixed" % (node))
+
+ if isinstance(node, ast.Assert):
+ # pytest assigns the position of the assertion to all expressions of the rewritten assertion.
+ # All the rewritten expressions get mapped to ast.Assert, which is the wrong ast-node.
+ # We don't report this wrong result.
+ raise KnownIssue("assert")
+
+ if any(isinstance(n, ast.pattern) for n in node_and_parents(node)):
+ # TODO: investigate
+ raise KnownIssue("pattern matching ranges seems to be wrong")
+
+ if instruction.opname == "STORE_NAME" and instruction.argval == "__classcell__":
+ # handle stores to __classcell__ as KnownIssue,
+ # because they get complicated if they are used in `if` or `for` loops
+ # example:
+ #
+ # class X:
+ # # ... something
+ # if some_condition:
+ # def method(self):
+ # super()
+ #
+ # The `STORE_NAME` instruction gets mapped to the `ast.If` node,
+ # because it is the last element in the class.
+ # This last element could be anything and gets dificult to verify.
+
+ raise KnownIssue("store __classcell__")
+
+ @staticmethod
+ def is_except_cleanup(inst: dis.Instruction, node: EnhancedAST) -> bool:
+ if inst.opname not in (
+ "STORE_NAME",
+ "STORE_FAST",
+ "STORE_DEREF",
+ "STORE_GLOBAL",
+ "DELETE_NAME",
+ "DELETE_FAST",
+ "DELETE_GLOBAL",
+ ):
+ return False
+
+ # This bytecode does something exception cleanup related.
+ # The position of the instruciton seems to be something in the last ast-node of the ExceptHandler
+ # this could be a bug, but it might not be observable in normal python code.
+
+ # example:
+ # except Exception as exc:
+ # enum_member._value_ = value
+
+ # other example:
+ # STORE_FAST of e was mapped to Constant(value=False)
+ # except OSError as e:
+ # if not _ignore_error(e):
+ # raise
+ # return False
+
+ # STORE_FAST of msg was mapped to print(...)
+ # except TypeError as msg:
+ # print("Sorry:", msg, file=file)
+
+ return any(
+ isinstance(n, ast.ExceptHandler) and mangled_name(n) == inst.argval
+ for n in parents(node)
+ )
+
+ def verify(self, node: EnhancedAST, instruction: dis.Instruction) -> None:
+ """
+ checks if this node could gererate this instruction
+ """
+
+ op_name = instruction.opname
+ extra_filter: Callable[[EnhancedAST], bool] = lambda e: True
+ ctx: Type = type(None)
+
+ def inst_match(opnames: Union[str, Sequence[str]], **kwargs: Any) -> bool:
+ """
+ match instruction
+
+ Parameters:
+ opnames: (str|Seq[str]): inst.opname has to be equal to or in `opname`
+ **kwargs: every arg has to match inst.arg
+
+ Returns:
+ True if all conditions match the instruction
+
+ """
+
+ if isinstance(opnames, str):
+ opnames = [opnames]
+ return instruction.opname in opnames and kwargs == {
+ k: getattr(instruction, k) for k in kwargs
+ }
+
+ def node_match(node_type: Union[Type, Tuple[Type, ...]], **kwargs: Any) -> bool:
+ """
+ match the ast-node
+
+ Parameters:
+ node_type: type of the node
+ **kwargs: every `arg` has to be equal `node.arg`
+ or `node.arg` has to be an instance of `arg` if it is a type.
+ """
+ return isinstance(node, node_type) and all(
+ isinstance(getattr(node, k), v)
+ if isinstance(v, type)
+ else getattr(node, k) == v
+ for k, v in kwargs.items()
+ )
+
+ if op_name == "CACHE":
+ return
+
+ if inst_match("CALL") and node_match((ast.With, ast.AsyncWith)):
+ # call to context.__exit__
+ return
+
+ if inst_match(("CALL", "LOAD_FAST")) and node_match(
+ (ast.ListComp, ast.GeneratorExp, ast.SetComp, ast.DictComp)
+ ):
+ # call to the generator function
+ return
+
+ if inst_match(("CALL", "CALL_FUNCTION_EX")) and node_match(
+ (ast.ClassDef, ast.Call)
+ ):
+ return
+
+ if inst_match(("COMPARE_OP", "IS_OP", "CONTAINS_OP")) and node_match(
+ ast.Compare
+ ):
+ return
+
+ if inst_match("LOAD_NAME", argval="__annotations__") and node_match(
+ ast.AnnAssign
+ ):
+ return
+
+ if (
+ (
+ inst_match("LOAD_METHOD", argval="join")
+ or inst_match(("CALL", "BUILD_STRING"))
+ )
+ and node_match(ast.BinOp, left=ast.Constant, op=ast.Mod)
+ and isinstance(cast(ast.Constant, cast(ast.BinOp, node).left).value, str)
+ ):
+ # "..."%(...) uses "".join
+ return
+
+ if inst_match("STORE_SUBSCR") and node_match(ast.AnnAssign):
+ # data: int
+ return
+
+ if self.is_except_cleanup(instruction, node):
+ return
+
+ if inst_match(("DELETE_NAME", "DELETE_FAST")) and node_match(
+ ast.Name, id=instruction.argval, ctx=ast.Del
+ ):
+ return
+
+ if inst_match("BUILD_STRING") and (
+ node_match(ast.JoinedStr) or node_match(ast.BinOp, op=ast.Mod)
+ ):
+ return
+
+ if inst_match(("BEFORE_WITH","WITH_EXCEPT_START")) and node_match(ast.With):
+ return
+
+ if inst_match(("STORE_NAME", "STORE_GLOBAL"), argval="__doc__") and node_match(
+ ast.Constant
+ ):
+ # store docstrings
+ return
+
+ if (
+ inst_match(("STORE_NAME", "STORE_FAST", "STORE_GLOBAL", "STORE_DEREF"))
+ and node_match(ast.ExceptHandler)
+ and instruction.argval == mangled_name(node)
+ ):
+ # store exception in variable
+ return
+
+ if (
+ inst_match(("STORE_NAME", "STORE_FAST", "STORE_DEREF", "STORE_GLOBAL"))
+ and node_match((ast.Import, ast.ImportFrom))
+ and any(mangled_name(cast(EnhancedAST, alias)) == instruction.argval for alias in cast(ast.Import, node).names)
+ ):
+ # store imported module in variable
+ return
+
+ if (
+ inst_match(("STORE_FAST", "STORE_DEREF", "STORE_NAME", "STORE_GLOBAL"))
+ and (
+ node_match((ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef))
+ or node_match(
+ ast.Name,
+ ctx=ast.Store,
+ )
+ )
+ and instruction.argval == mangled_name(node)
+ ):
+ return
+
+ if False:
+ # TODO: match expressions are not supported for now
+ if inst_match(("STORE_FAST", "STORE_NAME")) and node_match(
+ ast.MatchAs, name=instruction.argval
+ ):
+ return
+
+ if inst_match("COMPARE_OP", argval="==") and node_match(ast.MatchSequence):
+ return
+
+ if inst_match("COMPARE_OP", argval="==") and node_match(ast.MatchValue):
+ return
+
+ if inst_match("BINARY_OP") and node_match(
+ ast.AugAssign, op=op_type_map[instruction.argrepr.removesuffix("=")]
+ ):
+ # a+=5
+ return
+
+ if node_match(ast.Attribute, ctx=ast.Del) and inst_match(
+ "DELETE_ATTR", argval=mangled_name(node)
+ ):
+ return
+
+ if inst_match(("JUMP_IF_TRUE_OR_POP", "JUMP_IF_FALSE_OR_POP")) and node_match(
+ ast.BoolOp
+ ):
+ # and/or short circuit
+ return
+
+ if inst_match("DELETE_SUBSCR") and node_match(ast.Subscript, ctx=ast.Del):
+ return
+
+ if node_match(ast.Name, ctx=ast.Load) and inst_match(
+ ("LOAD_NAME", "LOAD_FAST", "LOAD_GLOBAL"), argval=mangled_name(node)
+ ):
+ return
+
+ if node_match(ast.Name, ctx=ast.Del) and inst_match(
+ ("DELETE_NAME", "DELETE_GLOBAL"), argval=mangled_name(node)
+ ):
+ return
+
+ # old verifier
+
+ typ: Type = type(None)
+ op_type: Type = type(None)
+
+ if op_name.startswith(("BINARY_SUBSCR", "SLICE+")):
+ typ = ast.Subscript
+ ctx = ast.Load
+ elif op_name.startswith("BINARY_"):
+ typ = ast.BinOp
+ op_type = op_type_map[instruction.argrepr]
+ extra_filter = lambda e: isinstance(cast(ast.BinOp, e).op, op_type)
+ elif op_name.startswith("UNARY_"):
+ typ = ast.UnaryOp
+ op_type = dict(
+ UNARY_POSITIVE=ast.UAdd,
+ UNARY_NEGATIVE=ast.USub,
+ UNARY_NOT=ast.Not,
+ UNARY_INVERT=ast.Invert,
+ )[op_name]
+ extra_filter = lambda e: isinstance(cast(ast.UnaryOp, e).op, op_type)
+ elif op_name in ("LOAD_ATTR", "LOAD_METHOD", "LOOKUP_METHOD"):
+ typ = ast.Attribute
+ ctx = ast.Load
+ extra_filter = lambda e: mangled_name(e) == instruction.argval
+ elif op_name in (
+ "LOAD_NAME",
+ "LOAD_GLOBAL",
+ "LOAD_FAST",
+ "LOAD_DEREF",
+ "LOAD_CLASSDEREF",
+ ):
+ typ = ast.Name
+ ctx = ast.Load
+ extra_filter = lambda e: cast(ast.Name, e).id == instruction.argval
+ elif op_name in ("COMPARE_OP", "IS_OP", "CONTAINS_OP"):
+ typ = ast.Compare
+ extra_filter = lambda e: len(cast(ast.Compare, e).ops) == 1
+ elif op_name.startswith(("STORE_SLICE", "STORE_SUBSCR")):
+ ctx = ast.Store
+ typ = ast.Subscript
+ elif op_name.startswith("STORE_ATTR"):
+ ctx = ast.Store
+ typ = ast.Attribute
+ extra_filter = lambda e: mangled_name(e) == instruction.argval
+
+ node_ctx = getattr(node, "ctx", None)
+
+ ctx_match = (
+ ctx is not type(None)
+ or not hasattr(node, "ctx")
+ or isinstance(node_ctx, ctx)
+ )
+
+ # check for old verifier
+ if isinstance(node, typ) and ctx_match and extra_filter(node):
+ return
+
+ # generate error
+
+ title = "ast.%s is not created from %s" % (
+ type(node).__name__,
+ instruction.opname,
+ )
+
+ raise VerifierFailure(title, node, instruction)
+
+ def instruction(self, index: int) -> dis.Instruction:
+ return self.bc_list[index // 2]
+
+ def opname(self, index: int) -> str:
+ return self.instruction(index).opname
+
+ def find_node(
+ self,
+ index: int,
+ match_positions: Sequence[str]=("lineno", "end_lineno", "col_offset", "end_col_offset"),
+ typ: tuple[Type, ...]=(ast.expr, ast.stmt, ast.excepthandler, ast.pattern),
+ ) -> EnhancedAST:
+ position = self.instruction(index).positions
+ assert position is not None and position.lineno is not None
+
+ return only(
+ cast(EnhancedAST, node)
+ for node in self.source._nodes_by_line[position.lineno]
+ if isinstance(node, typ)
+ if not isinstance(node, ast.Expr)
+ # matchvalue.value has the same positions as matchvalue themself, so we exclude ast.MatchValue
+ if not isinstance(node, ast.MatchValue)
+ if all(
+ getattr(position, attr) == getattr(node, attr)
+ for attr in match_positions
+ )
+ )
diff --git a/venv/lib/python3.10/site-packages/executing/executing.py b/venv/lib/python3.10/site-packages/executing/executing.py
new file mode 100644
index 000000000..c28091d3f
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/executing/executing.py
@@ -0,0 +1,1260 @@
+"""
+MIT License
+
+Copyright (c) 2021 Alex Hall
+
+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.
+"""
+
+import __future__
+import ast
+import dis
+import functools
+import inspect
+import io
+import linecache
+import re
+import sys
+import types
+from collections import defaultdict, namedtuple
+from copy import deepcopy
+from itertools import islice
+from operator import attrgetter
+from threading import RLock
+from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional, Sequence, Set, Sized, Tuple, Type, TypeVar, Union, cast
+
+if TYPE_CHECKING: # pragma: no cover
+ from asttokens import ASTTokens, ASTText
+ from asttokens.asttokens import ASTTextBase
+
+function_node_types = (ast.FunctionDef,) # type: Tuple[Type, ...]
+if sys.version_info[0] == 3:
+ function_node_types += (ast.AsyncFunctionDef,)
+
+
+if sys.version_info[0] == 3:
+ # noinspection PyUnresolvedReferences
+ from functools import lru_cache
+ # noinspection PyUnresolvedReferences
+ from tokenize import detect_encoding
+ from itertools import zip_longest
+ # noinspection PyUnresolvedReferences,PyCompatibility
+ from pathlib import Path
+
+ cache = lru_cache(maxsize=None)
+ text_type = str
+else:
+ from lib2to3.pgen2.tokenize import detect_encoding, cookie_re as encoding_pattern # type: ignore[attr-defined]
+ from itertools import izip_longest as zip_longest
+
+ class Path(object):
+ pass
+
+
+ def cache(func):
+ # type: (Callable) -> Callable
+ d = {} # type: Dict[Tuple, Callable]
+
+ @functools.wraps(func)
+ def wrapper(*args):
+ # type: (Any) -> Any
+ if args in d:
+ return d[args]
+ result = d[args] = func(*args)
+ return result
+
+ return wrapper
+
+
+ # noinspection PyUnresolvedReferences
+ text_type = unicode
+
+# Type class used to expand out the definition of AST to include fields added by this library
+# It's not actually used for anything other than type checking though!
+class EnhancedAST(ast.AST):
+ parent = None # type: EnhancedAST
+
+if sys.version_info >= (3, 4):
+ # noinspection PyUnresolvedReferences
+ _get_instructions = dis.get_instructions
+ from dis import Instruction as _Instruction
+
+ class Instruction(_Instruction):
+ lineno = None # type: int
+else:
+ class Instruction(namedtuple('Instruction', 'offset argval opname starts_line')):
+ lineno = None # type: int
+
+ from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname, findlinestarts, hasname
+
+ # Based on dis.disassemble from 2.7
+ # Left as similar as possible for easy diff
+
+ def _get_instructions(co):
+ # type: (types.CodeType) -> Iterator[Instruction]
+ code = co.co_code
+ linestarts = dict(findlinestarts(co))
+ n = len(code)
+ i = 0
+ extended_arg = 0
+ while i < n:
+ offset = i
+ c = code[i]
+ op = ord(c)
+ lineno = linestarts.get(i)
+ argval = None
+ i = i + 1
+ if op >= HAVE_ARGUMENT:
+ oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg
+ extended_arg = 0
+ i = i + 2
+ if op == EXTENDED_ARG:
+ extended_arg = oparg * 65536
+
+ if op in hasconst:
+ argval = co.co_consts[oparg]
+ elif op in hasname:
+ argval = co.co_names[oparg]
+ elif opname[op] == 'LOAD_FAST':
+ argval = co.co_varnames[oparg]
+ yield Instruction(offset, argval, opname[op], lineno)
+
+
+# Type class used to expand out the definition of AST to include fields added by this library
+# It's not actually used for anything other than type checking though!
+class EnhancedInstruction(Instruction):
+ _copied = None # type: bool
+
+
+
+def assert_(condition, message=""):
+ # type: (Any, str) -> None
+ """
+ Like an assert statement, but unaffected by -O
+ :param condition: value that is expected to be truthy
+ :type message: Any
+ """
+ if not condition:
+ raise AssertionError(str(message))
+
+
+def get_instructions(co):
+ # type: (types.CodeType) -> Iterator[EnhancedInstruction]
+ lineno = co.co_firstlineno
+ for inst in _get_instructions(co):
+ inst = cast(EnhancedInstruction, inst)
+ lineno = inst.starts_line or lineno
+ assert_(lineno)
+ inst.lineno = lineno
+ yield inst
+
+
+TESTING = 0
+
+
+class NotOneValueFound(Exception):
+ def __init__(self,msg,values=[]):
+ # type: (str, Sequence) -> None
+ self.values=values
+ super(NotOneValueFound,self).__init__(msg)
+
+T = TypeVar('T')
+
+
+def only(it):
+ # type: (Iterable[T]) -> T
+ if isinstance(it, Sized):
+ if len(it) != 1:
+ raise NotOneValueFound('Expected one value, found %s' % len(it))
+ # noinspection PyTypeChecker
+ return list(it)[0]
+
+ lst = tuple(islice(it, 2))
+ if len(lst) == 0:
+ raise NotOneValueFound('Expected one value, found 0')
+ if len(lst) > 1:
+ raise NotOneValueFound('Expected one value, found several',lst)
+ return lst[0]
+
+
+class Source(object):
+ """
+ The source code of a single file and associated metadata.
+
+ The main method of interest is the classmethod `executing(frame)`.
+
+ If you want an instance of this class, don't construct it.
+ Ideally use the classmethod `for_frame(frame)`.
+ If you don't have a frame, use `for_filename(filename [, module_globals])`.
+ These methods cache instances by filename, so at most one instance exists per filename.
+
+ Attributes:
+ - filename
+ - text
+ - lines
+ - tree: AST parsed from text, or None if text is not valid Python
+ All nodes in the tree have an extra `parent` attribute
+
+ Other methods of interest:
+ - statements_at_line
+ - asttokens
+ - code_qualname
+ """
+
+ def __init__(self, filename, lines):
+ # type: (str, Sequence[str]) -> None
+ """
+ Don't call this constructor, see the class docstring.
+ """
+
+ self.filename = filename
+ text = ''.join(lines)
+
+ if not isinstance(text, text_type):
+ encoding = self.detect_encoding(text)
+ # noinspection PyUnresolvedReferences
+ text = text.decode(encoding)
+ lines = [line.decode(encoding) for line in lines]
+
+ self.text = text
+ self.lines = [line.rstrip('\r\n') for line in lines]
+
+ if sys.version_info[0] == 3:
+ ast_text = text
+ else:
+ # In python 2 it's a syntax error to parse unicode
+ # with an encoding declaration, so we remove it but
+ # leave empty lines in its place to keep line numbers the same
+ ast_text = ''.join([
+ '\n' if i < 2 and encoding_pattern.match(line)
+ else line
+ for i, line in enumerate(lines)
+ ])
+
+ self._nodes_by_line = defaultdict(list)
+ self.tree = None
+ self._qualnames = {}
+ self._asttokens = None # type: Optional[ASTTokens]
+ self._asttext = None # type: Optional[ASTText]
+
+ try:
+ self.tree = ast.parse(ast_text, filename=filename)
+ except (SyntaxError, ValueError):
+ pass
+ else:
+ for node in ast.walk(self.tree):
+ for child in ast.iter_child_nodes(node):
+ cast(EnhancedAST, child).parent = cast(EnhancedAST, node)
+ for lineno in node_linenos(node):
+ self._nodes_by_line[lineno].append(node)
+
+ visitor = QualnameVisitor()
+ visitor.visit(self.tree)
+ self._qualnames = visitor.qualnames
+
+ @classmethod
+ def for_frame(cls, frame, use_cache=True):
+ # type: (types.FrameType, bool) -> "Source"
+ """
+ Returns the `Source` object corresponding to the file the frame is executing in.
+ """
+ return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {}, use_cache)
+
+ @classmethod
+ def for_filename(
+ cls,
+ filename,
+ module_globals=None,
+ use_cache=True, # noqa no longer used
+ ):
+ # type: (Union[str, Path], Optional[Dict[str, Any]], bool) -> "Source"
+ if isinstance(filename, Path):
+ filename = str(filename)
+
+ def get_lines():
+ # type: () -> List[str]
+ return linecache.getlines(cast(text_type, filename), module_globals)
+
+ # Save the current linecache entry, then ensure the cache is up to date.
+ entry = linecache.cache.get(filename) # type: ignore[attr-defined]
+ linecache.checkcache(filename)
+ lines = get_lines()
+ if entry is not None and not lines:
+ # There was an entry, checkcache removed it, and nothing replaced it.
+ # This means the file wasn't simply changed (because the `lines` wouldn't be empty)
+ # but rather the file was found not to exist, probably because `filename` was fake.
+ # Restore the original entry so that we still have something.
+ linecache.cache[filename] = entry # type: ignore[attr-defined]
+ lines = get_lines()
+
+ return cls._for_filename_and_lines(filename, tuple(lines))
+
+ @classmethod
+ def _for_filename_and_lines(cls, filename, lines):
+ # type: (str, Sequence[str]) -> "Source"
+ source_cache = cls._class_local('__source_cache_with_lines', {}) # type: Dict[Tuple[str, Sequence[str]], Source]
+ try:
+ return source_cache[(filename, lines)]
+ except KeyError:
+ pass
+
+ result = source_cache[(filename, lines)] = cls(filename, lines)
+ return result
+
+ @classmethod
+ def lazycache(cls, frame):
+ # type: (types.FrameType) -> None
+ if sys.version_info >= (3, 5):
+ linecache.lazycache(frame.f_code.co_filename, frame.f_globals)
+
+ @classmethod
+ def executing(cls, frame_or_tb):
+ # type: (Union[types.TracebackType, types.FrameType]) -> "Executing"
+ """
+ Returns an `Executing` object representing the operation
+ currently executing in the given frame or traceback object.
+ """
+ if isinstance(frame_or_tb, types.TracebackType):
+ # https://docs.python.org/3/reference/datamodel.html#traceback-objects
+ # "tb_lineno gives the line number where the exception occurred;
+ # tb_lasti indicates the precise instruction.
+ # The line number and last instruction in the traceback may differ
+ # from the line number of its frame object
+ # if the exception occurred in a try statement with no matching except clause
+ # or with a finally clause."
+ tb = frame_or_tb
+ frame = tb.tb_frame
+ lineno = tb.tb_lineno
+ lasti = tb.tb_lasti
+ else:
+ frame = frame_or_tb
+ lineno = frame.f_lineno
+ lasti = frame.f_lasti
+
+
+
+ code = frame.f_code
+ key = (code, id(code), lasti)
+ executing_cache = cls._class_local('__executing_cache', {}) # type: Dict[Tuple[types.CodeType, int, int], Any]
+
+ args = executing_cache.get(key)
+ if not args:
+ node = stmts = decorator = None
+ source = cls.for_frame(frame)
+ tree = source.tree
+ if tree:
+ try:
+ stmts = source.statements_at_line(lineno)
+ if stmts:
+ if is_ipython_cell_code(code):
+ decorator, node = find_node_ipython(frame, lasti, stmts, source)
+ else:
+ node_finder = NodeFinder(frame, stmts, tree, lasti, source)
+ node = node_finder.result
+ decorator = node_finder.decorator
+ except Exception:
+ if TESTING:
+ raise
+
+ assert stmts is not None
+ if node:
+ new_stmts = {statement_containing_node(node)}
+ assert_(new_stmts <= stmts)
+ stmts = new_stmts
+
+ executing_cache[key] = args = source, node, stmts, decorator
+
+ return Executing(frame, *args)
+
+ @classmethod
+ def _class_local(cls, name, default):
+ # type: (str, T) -> T
+ """
+ Returns an attribute directly associated with this class
+ (as opposed to subclasses), setting default if necessary
+ """
+ # classes have a mappingproxy preventing us from using setdefault
+ result = cls.__dict__.get(name, default)
+ setattr(cls, name, result)
+ return result
+
+ @cache
+ def statements_at_line(self, lineno):
+ # type: (int) -> Set[EnhancedAST]
+ """
+ Returns the statement nodes overlapping the given line.
+
+ Returns at most one statement unless semicolons are present.
+
+ If the `text` attribute is not valid python, meaning
+ `tree` is None, returns an empty set.
+
+ Otherwise, `Source.for_frame(frame).statements_at_line(frame.f_lineno)`
+ should return at least one statement.
+ """
+
+ return {
+ statement_containing_node(node)
+ for node in
+ self._nodes_by_line[lineno]
+ }
+
+ def asttext(self):
+ # type: () -> ASTText
+ """
+ Returns an ASTText object for getting the source of specific AST nodes.
+
+ See http://asttokens.readthedocs.io/en/latest/api-index.html
+ """
+ from asttokens import ASTText # must be installed separately
+
+ if self._asttext is None:
+ self._asttext = ASTText(self.text, tree=self.tree, filename=self.filename)
+
+ return self._asttext
+
+ def asttokens(self):
+ # type: () -> ASTTokens
+ """
+ Returns an ASTTokens object for getting the source of specific AST nodes.
+
+ See http://asttokens.readthedocs.io/en/latest/api-index.html
+ """
+ import asttokens # must be installed separately
+
+ if self._asttokens is None:
+ if hasattr(asttokens, 'ASTText'):
+ self._asttokens = self.asttext().asttokens
+ else: # pragma: no cover
+ self._asttokens = asttokens.ASTTokens(self.text, tree=self.tree, filename=self.filename)
+ return self._asttokens
+
+ def _asttext_base(self):
+ # type: () -> ASTTextBase
+ import asttokens # must be installed separately
+
+ if hasattr(asttokens, 'ASTText'):
+ return self.asttext()
+ else: # pragma: no cover
+ return self.asttokens()
+
+ @staticmethod
+ def decode_source(source):
+ # type: (Union[str, bytes]) -> text_type
+ if isinstance(source, bytes):
+ encoding = Source.detect_encoding(source)
+ return source.decode(encoding)
+ else:
+ return source
+
+ @staticmethod
+ def detect_encoding(source):
+ # type: (bytes) -> str
+ return detect_encoding(io.BytesIO(source).readline)[0]
+
+ def code_qualname(self, code):
+ # type: (types.CodeType) -> str
+ """
+ Imitates the __qualname__ attribute of functions for code objects.
+ Given:
+
+ - A function `func`
+ - A frame `frame` for an execution of `func`, meaning:
+ `frame.f_code is func.__code__`
+
+ `Source.for_frame(frame).code_qualname(frame.f_code)`
+ will be equal to `func.__qualname__`*. Works for Python 2 as well,
+ where of course no `__qualname__` attribute exists.
+
+ Falls back to `code.co_name` if there is no appropriate qualname.
+
+ Based on https://github.com/wbolster/qualname
+
+ (* unless `func` is a lambda
+ nested inside another lambda on the same line, in which case
+ the outer lambda's qualname will be returned for the codes
+ of both lambdas)
+ """
+ assert_(code.co_filename == self.filename)
+ return self._qualnames.get((code.co_name, code.co_firstlineno), code.co_name)
+
+
+class Executing(object):
+ """
+ Information about the operation a frame is currently executing.
+
+ Generally you will just want `node`, which is the AST node being executed,
+ or None if it's unknown.
+
+ If a decorator is currently being called, then:
+ - `node` is a function or class definition
+ - `decorator` is the expression in `node.decorator_list` being called
+ - `statements == {node}`
+ """
+
+ def __init__(self, frame, source, node, stmts, decorator):
+ # type: (types.FrameType, Source, EnhancedAST, Set[ast.stmt], Optional[EnhancedAST]) -> None
+ self.frame = frame
+ self.source = source
+ self.node = node
+ self.statements = stmts
+ self.decorator = decorator
+
+ def code_qualname(self):
+ # type: () -> str
+ return self.source.code_qualname(self.frame.f_code)
+
+ def text(self):
+ # type: () -> str
+ return self.source._asttext_base().get_text(self.node)
+
+ def text_range(self):
+ # type: () -> Tuple[int, int]
+ return self.source._asttext_base().get_text_range(self.node)
+
+
+class QualnameVisitor(ast.NodeVisitor):
+ def __init__(self):
+ # type: () -> None
+ super(QualnameVisitor, self).__init__()
+ self.stack = [] # type: List[str]
+ self.qualnames = {} # type: Dict[Tuple[str, int], str]
+
+ def add_qualname(self, node, name=None):
+ # type: (ast.AST, Optional[str]) -> None
+ name = name or node.name # type: ignore[attr-defined]
+ self.stack.append(name)
+ if getattr(node, 'decorator_list', ()):
+ lineno = node.decorator_list[0].lineno # type: ignore[attr-defined]
+ else:
+ lineno = node.lineno # type: ignore[attr-defined]
+ self.qualnames.setdefault((name, lineno), ".".join(self.stack))
+
+ def visit_FunctionDef(self, node, name=None):
+ # type: (ast.AST, Optional[str]) -> None
+ if sys.version_info[0] == 3:
+ assert isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda)), node
+ else:
+ assert isinstance(node, (ast.FunctionDef, ast.Lambda)), node
+ self.add_qualname(node, name)
+ self.stack.append('')
+ children = [] # type: Sequence[ast.AST]
+ if isinstance(node, ast.Lambda):
+ children = [node.body]
+ else:
+ children = node.body
+ for child in children:
+ self.visit(child)
+ self.stack.pop()
+ self.stack.pop()
+
+ # Find lambdas in the function definition outside the body,
+ # e.g. decorators or default arguments
+ # Based on iter_child_nodes
+ for field, child in ast.iter_fields(node):
+ if field == 'body':
+ continue
+ if isinstance(child, ast.AST):
+ self.visit(child)
+ elif isinstance(child, list):
+ for grandchild in child:
+ if isinstance(grandchild, ast.AST):
+ self.visit(grandchild)
+
+ visit_AsyncFunctionDef = visit_FunctionDef
+
+ def visit_Lambda(self, node):
+ # type: (ast.AST) -> None
+ assert isinstance(node, ast.Lambda)
+ self.visit_FunctionDef(node, '')
+
+ def visit_ClassDef(self, node):
+ # type: (ast.AST) -> None
+ assert isinstance(node, ast.ClassDef)
+ self.add_qualname(node)
+ self.generic_visit(node)
+ self.stack.pop()
+
+
+
+
+
+future_flags = sum(
+ getattr(__future__, fname).compiler_flag for fname in __future__.all_feature_names
+)
+
+
+def compile_similar_to(source, matching_code):
+ # type: (ast.Module, types.CodeType) -> Any
+ return compile(
+ source,
+ matching_code.co_filename,
+ 'exec',
+ flags=future_flags & matching_code.co_flags,
+ dont_inherit=True,
+ )
+
+
+sentinel = 'io8urthglkjdghvljusketgIYRFYUVGHFRTBGVHKGF78678957647698'
+
+def is_rewritten_by_pytest(code):
+ # type: (types.CodeType) -> bool
+ return any(
+ bc.opname != "LOAD_CONST" and isinstance(bc.argval,str) and bc.argval.startswith("@py")
+ for bc in get_instructions(code)
+ )
+
+
+class SentinelNodeFinder(object):
+ result = None # type: EnhancedAST
+
+ def __init__(self, frame, stmts, tree, lasti, source):
+ # type: (types.FrameType, Set[EnhancedAST], ast.Module, int, Source) -> None
+ assert_(stmts)
+ self.frame = frame
+ self.tree = tree
+ self.code = code = frame.f_code
+ self.is_pytest = is_rewritten_by_pytest(code)
+
+ if self.is_pytest:
+ self.ignore_linenos = frozenset(assert_linenos(tree))
+ else:
+ self.ignore_linenos = frozenset()
+
+ self.decorator = None
+
+ self.instruction = instruction = self.get_actual_current_instruction(lasti)
+ op_name = instruction.opname
+ extra_filter = lambda e: True # type: Callable[[Any], bool]
+ ctx = type(None) # type: Type
+
+ typ = type(None) # type: Type
+ if op_name.startswith('CALL_'):
+ typ = ast.Call
+ elif op_name.startswith(('BINARY_SUBSCR', 'SLICE+')):
+ typ = ast.Subscript
+ ctx = ast.Load
+ elif op_name.startswith('BINARY_'):
+ typ = ast.BinOp
+ op_type = dict(
+ BINARY_POWER=ast.Pow,
+ BINARY_MULTIPLY=ast.Mult,
+ BINARY_MATRIX_MULTIPLY=getattr(ast, "MatMult", ()),
+ BINARY_FLOOR_DIVIDE=ast.FloorDiv,
+ BINARY_TRUE_DIVIDE=ast.Div,
+ BINARY_MODULO=ast.Mod,
+ BINARY_ADD=ast.Add,
+ BINARY_SUBTRACT=ast.Sub,
+ BINARY_LSHIFT=ast.LShift,
+ BINARY_RSHIFT=ast.RShift,
+ BINARY_AND=ast.BitAnd,
+ BINARY_XOR=ast.BitXor,
+ BINARY_OR=ast.BitOr,
+ )[op_name]
+ extra_filter = lambda e: isinstance(e.op, op_type)
+ elif op_name.startswith('UNARY_'):
+ typ = ast.UnaryOp
+ op_type = dict(
+ UNARY_POSITIVE=ast.UAdd,
+ UNARY_NEGATIVE=ast.USub,
+ UNARY_NOT=ast.Not,
+ UNARY_INVERT=ast.Invert,
+ )[op_name]
+ extra_filter = lambda e: isinstance(e.op, op_type)
+ elif op_name in ('LOAD_ATTR', 'LOAD_METHOD', 'LOOKUP_METHOD'):
+ typ = ast.Attribute
+ ctx = ast.Load
+ extra_filter = lambda e: attr_names_match(e.attr, instruction.argval)
+ elif op_name in ('LOAD_NAME', 'LOAD_GLOBAL', 'LOAD_FAST', 'LOAD_DEREF', 'LOAD_CLASSDEREF'):
+ typ = ast.Name
+ ctx = ast.Load
+ if sys.version_info[0] == 3 or instruction.argval:
+ extra_filter = lambda e: e.id == instruction.argval
+ elif op_name in ('COMPARE_OP', 'IS_OP', 'CONTAINS_OP'):
+ typ = ast.Compare
+ extra_filter = lambda e: len(e.ops) == 1
+ elif op_name.startswith(('STORE_SLICE', 'STORE_SUBSCR')):
+ ctx = ast.Store
+ typ = ast.Subscript
+ elif op_name.startswith('STORE_ATTR'):
+ ctx = ast.Store
+ typ = ast.Attribute
+ extra_filter = lambda e: attr_names_match(e.attr, instruction.argval)
+ else:
+ raise RuntimeError(op_name)
+
+ with lock:
+ exprs = {
+ cast(EnhancedAST, node)
+ for stmt in stmts
+ for node in ast.walk(stmt)
+ if isinstance(node, typ)
+ if isinstance(getattr(node, "ctx", None), ctx)
+ if extra_filter(node)
+ if statement_containing_node(node) == stmt
+ }
+
+ if ctx == ast.Store:
+ # No special bytecode tricks here.
+ # We can handle multiple assigned attributes with different names,
+ # but only one assigned subscript.
+ self.result = only(exprs)
+ return
+
+ matching = list(self.matching_nodes(exprs))
+ if not matching and typ == ast.Call:
+ self.find_decorator(stmts)
+ else:
+ self.result = only(matching)
+
+ def find_decorator(self, stmts):
+ # type: (Union[List[EnhancedAST], Set[EnhancedAST]]) -> None
+ stmt = only(stmts)
+ assert_(isinstance(stmt, (ast.ClassDef, function_node_types)))
+ decorators = stmt.decorator_list # type: ignore[attr-defined]
+ assert_(decorators)
+ line_instructions = [
+ inst
+ for inst in self.clean_instructions(self.code)
+ if inst.lineno == self.frame.f_lineno
+ ]
+ last_decorator_instruction_index = [
+ i
+ for i, inst in enumerate(line_instructions)
+ if inst.opname == "CALL_FUNCTION"
+ ][-1]
+ assert_(
+ line_instructions[last_decorator_instruction_index + 1].opname.startswith(
+ "STORE_"
+ )
+ )
+ decorator_instructions = line_instructions[
+ last_decorator_instruction_index
+ - len(decorators)
+ + 1 : last_decorator_instruction_index
+ + 1
+ ]
+ assert_({inst.opname for inst in decorator_instructions} == {"CALL_FUNCTION"})
+ decorator_index = decorator_instructions.index(self.instruction)
+ decorator = decorators[::-1][decorator_index]
+ self.decorator = decorator
+ self.result = stmt
+
+ def clean_instructions(self, code):
+ # type: (types.CodeType) -> List[EnhancedInstruction]
+ return [
+ inst
+ for inst in get_instructions(code)
+ if inst.opname not in ("EXTENDED_ARG", "NOP")
+ if inst.lineno not in self.ignore_linenos
+ ]
+
+ def get_original_clean_instructions(self):
+ # type: () -> List[EnhancedInstruction]
+ result = self.clean_instructions(self.code)
+
+ # pypy sometimes (when is not clear)
+ # inserts JUMP_IF_NOT_DEBUG instructions in bytecode
+ # If they're not present in our compiled instructions,
+ # ignore them in the original bytecode
+ if not any(
+ inst.opname == "JUMP_IF_NOT_DEBUG"
+ for inst in self.compile_instructions()
+ ):
+ result = [
+ inst for inst in result
+ if inst.opname != "JUMP_IF_NOT_DEBUG"
+ ]
+
+ return result
+
+ def matching_nodes(self, exprs):
+ # type: (Set[EnhancedAST]) -> Iterator[EnhancedAST]
+ original_instructions = self.get_original_clean_instructions()
+ original_index = only(
+ i
+ for i, inst in enumerate(original_instructions)
+ if inst == self.instruction
+ )
+ for expr_index, expr in enumerate(exprs):
+ setter = get_setter(expr)
+ assert setter is not None
+ # noinspection PyArgumentList
+ replacement = ast.BinOp(
+ left=expr,
+ op=ast.Pow(),
+ right=ast.Str(s=sentinel),
+ )
+ ast.fix_missing_locations(replacement)
+ setter(replacement)
+ try:
+ instructions = self.compile_instructions()
+ finally:
+ setter(expr)
+
+ if sys.version_info >= (3, 10):
+ try:
+ handle_jumps(instructions, original_instructions)
+ except Exception:
+ # Give other candidates a chance
+ if TESTING or expr_index < len(exprs) - 1:
+ continue
+ raise
+
+ indices = [
+ i
+ for i, instruction in enumerate(instructions)
+ if instruction.argval == sentinel
+ ]
+
+ # There can be several indices when the bytecode is duplicated,
+ # as happens in a finally block in 3.9+
+ # First we remove the opcodes caused by our modifications
+ for index_num, sentinel_index in enumerate(indices):
+ # Adjustment for removing sentinel instructions below
+ # in past iterations
+ sentinel_index -= index_num * 2
+
+ assert_(instructions.pop(sentinel_index).opname == 'LOAD_CONST')
+ assert_(instructions.pop(sentinel_index).opname == 'BINARY_POWER')
+
+ # Then we see if any of the instruction indices match
+ for index_num, sentinel_index in enumerate(indices):
+ sentinel_index -= index_num * 2
+ new_index = sentinel_index - 1
+
+ if new_index != original_index:
+ continue
+
+ original_inst = original_instructions[original_index]
+ new_inst = instructions[new_index]
+
+ # In Python 3.9+, changing 'not x in y' to 'not sentinel_transformation(x in y)'
+ # changes a CONTAINS_OP(invert=1) to CONTAINS_OP(invert=0),,UNARY_NOT
+ if (
+ original_inst.opname == new_inst.opname in ('CONTAINS_OP', 'IS_OP')
+ and original_inst.arg != new_inst.arg # type: ignore[attr-defined]
+ and (
+ original_instructions[original_index + 1].opname
+ != instructions[new_index + 1].opname == 'UNARY_NOT'
+ )):
+ # Remove the difference for the upcoming assert
+ instructions.pop(new_index + 1)
+
+ # Check that the modified instructions don't have anything unexpected
+ # 3.10 is a bit too weird to assert this in all cases but things still work
+ if sys.version_info < (3, 10):
+ for inst1, inst2 in zip_longest(
+ original_instructions, instructions
+ ):
+ assert_(inst1 and inst2 and opnames_match(inst1, inst2))
+
+ yield expr
+
+ def compile_instructions(self):
+ # type: () -> List[EnhancedInstruction]
+ module_code = compile_similar_to(self.tree, self.code)
+ code = only(self.find_codes(module_code))
+ return self.clean_instructions(code)
+
+ def find_codes(self, root_code):
+ # type: (types.CodeType) -> list
+ checks = [
+ attrgetter('co_firstlineno'),
+ attrgetter('co_freevars'),
+ attrgetter('co_cellvars'),
+ lambda c: is_ipython_cell_code_name(c.co_name) or c.co_name,
+ ] # type: List[Callable]
+ if not self.is_pytest:
+ checks += [
+ attrgetter('co_names'),
+ attrgetter('co_varnames'),
+ ]
+
+ def matches(c):
+ # type: (types.CodeType) -> bool
+ return all(
+ f(c) == f(self.code)
+ for f in checks
+ )
+
+ code_options = []
+ if matches(root_code):
+ code_options.append(root_code)
+
+ def finder(code):
+ # type: (types.CodeType) -> None
+ for const in code.co_consts:
+ if not inspect.iscode(const):
+ continue
+
+ if matches(const):
+ code_options.append(const)
+ finder(const)
+
+ finder(root_code)
+ return code_options
+
+ def get_actual_current_instruction(self, lasti):
+ # type: (int) -> EnhancedInstruction
+ """
+ Get the instruction corresponding to the current
+ frame offset, skipping EXTENDED_ARG instructions
+ """
+ # Don't use get_original_clean_instructions
+ # because we need the actual instructions including
+ # EXTENDED_ARG
+ instructions = list(get_instructions(self.code))
+ index = only(
+ i
+ for i, inst in enumerate(instructions)
+ if inst.offset == lasti
+ )
+
+ while True:
+ instruction = instructions[index]
+ if instruction.opname != "EXTENDED_ARG":
+ return instruction
+ index += 1
+
+
+
+def non_sentinel_instructions(instructions, start):
+ # type: (List[EnhancedInstruction], int) -> Iterator[Tuple[int, EnhancedInstruction]]
+ """
+ Yields (index, instruction) pairs excluding the basic
+ instructions introduced by the sentinel transformation
+ """
+ skip_power = False
+ for i, inst in islice(enumerate(instructions), start, None):
+ if inst.argval == sentinel:
+ assert_(inst.opname == "LOAD_CONST")
+ skip_power = True
+ continue
+ elif skip_power:
+ assert_(inst.opname == "BINARY_POWER")
+ skip_power = False
+ continue
+ yield i, inst
+
+
+def walk_both_instructions(original_instructions, original_start, instructions, start):
+ # type: (List[EnhancedInstruction], int, List[EnhancedInstruction], int) -> Iterator[Tuple[int, EnhancedInstruction, int, EnhancedInstruction]]
+ """
+ Yields matching indices and instructions from the new and original instructions,
+ leaving out changes made by the sentinel transformation.
+ """
+ original_iter = islice(enumerate(original_instructions), original_start, None)
+ new_iter = non_sentinel_instructions(instructions, start)
+ inverted_comparison = False
+ while True:
+ try:
+ original_i, original_inst = next(original_iter)
+ new_i, new_inst = next(new_iter)
+ except StopIteration:
+ return
+ if (
+ inverted_comparison
+ and original_inst.opname != new_inst.opname == "UNARY_NOT"
+ ):
+ new_i, new_inst = next(new_iter)
+ inverted_comparison = (
+ original_inst.opname == new_inst.opname in ("CONTAINS_OP", "IS_OP")
+ and original_inst.arg != new_inst.arg # type: ignore[attr-defined]
+ )
+ yield original_i, original_inst, new_i, new_inst
+
+
+def handle_jumps(instructions, original_instructions):
+ # type: (List[EnhancedInstruction], List[EnhancedInstruction]) -> None
+ """
+ Transforms instructions in place until it looks more like original_instructions.
+ This is only needed in 3.10+ where optimisations lead to more drastic changes
+ after the sentinel transformation.
+ Replaces JUMP instructions that aren't also present in original_instructions
+ with the sections that they jump to until a raise or return.
+ In some other cases duplication found in `original_instructions`
+ is replicated in `instructions`.
+ """
+ while True:
+ for original_i, original_inst, new_i, new_inst in walk_both_instructions(
+ original_instructions, 0, instructions, 0
+ ):
+ if opnames_match(original_inst, new_inst):
+ continue
+
+ if "JUMP" in new_inst.opname and "JUMP" not in original_inst.opname:
+ # Find where the new instruction is jumping to, ignoring
+ # instructions which have been copied in previous iterations
+ start = only(
+ i
+ for i, inst in enumerate(instructions)
+ if inst.offset == new_inst.argval
+ and not getattr(inst, "_copied", False)
+ )
+ # Replace the jump instruction with the jumped to section of instructions
+ # That section may also be deleted if it's not similarly duplicated
+ # in original_instructions
+ new_instructions = handle_jump(
+ original_instructions, original_i, instructions, start
+ )
+ assert new_instructions is not None
+ instructions[new_i : new_i + 1] = new_instructions
+ else:
+ # Extract a section of original_instructions from original_i to return/raise
+ orig_section = []
+ for section_inst in original_instructions[original_i:]:
+ orig_section.append(section_inst)
+ if section_inst.opname in ("RETURN_VALUE", "RAISE_VARARGS"):
+ break
+ else:
+ # No return/raise - this is just a mismatch we can't handle
+ raise AssertionError
+
+ instructions[new_i:new_i] = only(find_new_matching(orig_section, instructions))
+
+ # instructions has been modified, the for loop can't sensibly continue
+ # Restart it from the beginning, checking for other issues
+ break
+
+ else: # No mismatched jumps found, we're done
+ return
+
+
+def find_new_matching(orig_section, instructions):
+ # type: (List[EnhancedInstruction], List[EnhancedInstruction]) -> Iterator[List[EnhancedInstruction]]
+ """
+ Yields sections of `instructions` which match `orig_section`.
+ The yielded sections include sentinel instructions, but these
+ are ignored when checking for matches.
+ """
+ for start in range(len(instructions) - len(orig_section)):
+ indices, dup_section = zip(
+ *islice(
+ non_sentinel_instructions(instructions, start),
+ len(orig_section),
+ )
+ )
+ if len(dup_section) < len(orig_section):
+ return
+ if sections_match(orig_section, dup_section):
+ yield instructions[start:indices[-1] + 1]
+
+
+def handle_jump(original_instructions, original_start, instructions, start):
+ # type: (List[EnhancedInstruction], int, List[EnhancedInstruction], int) -> Optional[List[EnhancedInstruction]]
+ """
+ Returns the section of instructions starting at `start` and ending
+ with a RETURN_VALUE or RAISE_VARARGS instruction.
+ There should be a matching section in original_instructions starting at original_start.
+ If that section doesn't appear elsewhere in original_instructions,
+ then also delete the returned section of instructions.
+ """
+ for original_j, original_inst, new_j, new_inst in walk_both_instructions(
+ original_instructions, original_start, instructions, start
+ ):
+ assert_(opnames_match(original_inst, new_inst))
+ if original_inst.opname in ("RETURN_VALUE", "RAISE_VARARGS"):
+ inlined = deepcopy(instructions[start : new_j + 1])
+ for inl in inlined:
+ inl._copied = True
+ orig_section = original_instructions[original_start : original_j + 1]
+ if not check_duplicates(
+ original_start, orig_section, original_instructions
+ ):
+ instructions[start : new_j + 1] = []
+ return inlined
+
+ return None
+
+
+def check_duplicates(original_i, orig_section, original_instructions):
+ # type: (int, List[EnhancedInstruction], List[EnhancedInstruction]) -> bool
+ """
+ Returns True if a section of original_instructions starting somewhere other
+ than original_i and matching orig_section is found, i.e. orig_section is duplicated.
+ """
+ for dup_start in range(len(original_instructions)):
+ if dup_start == original_i:
+ continue
+ dup_section = original_instructions[dup_start : dup_start + len(orig_section)]
+ if len(dup_section) < len(orig_section):
+ return False
+ if sections_match(orig_section, dup_section):
+ return True
+
+ return False
+
+def sections_match(orig_section, dup_section):
+ # type: (List[EnhancedInstruction], List[EnhancedInstruction]) -> bool
+ """
+ Returns True if the given lists of instructions have matching linenos and opnames.
+ """
+ return all(
+ (
+ orig_inst.lineno == dup_inst.lineno
+ # POP_BLOCKs have been found to have differing linenos in innocent cases
+ or "POP_BLOCK" == orig_inst.opname == dup_inst.opname
+ )
+ and opnames_match(orig_inst, dup_inst)
+ for orig_inst, dup_inst in zip(orig_section, dup_section)
+ )
+
+
+def opnames_match(inst1, inst2):
+ # type: (Instruction, Instruction) -> bool
+ return (
+ inst1.opname == inst2.opname
+ or "JUMP" in inst1.opname
+ and "JUMP" in inst2.opname
+ or (inst1.opname == "PRINT_EXPR" and inst2.opname == "POP_TOP")
+ or (
+ inst1.opname in ("LOAD_METHOD", "LOOKUP_METHOD")
+ and inst2.opname == "LOAD_ATTR"
+ )
+ or (inst1.opname == "CALL_METHOD" and inst2.opname == "CALL_FUNCTION")
+ )
+
+
+def get_setter(node):
+ # type: (EnhancedAST) -> Optional[Callable[[ast.AST], None]]
+ parent = node.parent
+ for name, field in ast.iter_fields(parent):
+ if field is node:
+ def setter(new_node):
+ # type: (ast.AST) -> None
+ return setattr(parent, name, new_node)
+ return setter
+ elif isinstance(field, list):
+ for i, item in enumerate(field):
+ if item is node:
+ def setter(new_node):
+ # type: (ast.AST) -> None
+ field[i] = new_node
+
+ return setter
+ return None
+
+lock = RLock()
+
+
+@cache
+def statement_containing_node(node):
+ # type: (ast.AST) -> EnhancedAST
+ while not isinstance(node, ast.stmt):
+ node = cast(EnhancedAST, node).parent
+ return cast(EnhancedAST, node)
+
+
+def assert_linenos(tree):
+ # type: (ast.AST) -> Iterator[int]
+ for node in ast.walk(tree):
+ if (
+ hasattr(node, 'parent') and
+ isinstance(statement_containing_node(node), ast.Assert)
+ ):
+ for lineno in node_linenos(node):
+ yield lineno
+
+
+def _extract_ipython_statement(stmt):
+ # type: (EnhancedAST) -> ast.Module
+ # IPython separates each statement in a cell to be executed separately
+ # So NodeFinder should only compile one statement at a time or it
+ # will find a code mismatch.
+ while not isinstance(stmt.parent, ast.Module):
+ stmt = stmt.parent
+ # use `ast.parse` instead of `ast.Module` for better portability
+ # python3.8 changes the signature of `ast.Module`
+ # Inspired by https://github.com/pallets/werkzeug/pull/1552/files
+ tree = ast.parse("")
+ tree.body = [cast(ast.stmt, stmt)]
+ ast.copy_location(tree, stmt)
+ return tree
+
+
+def is_ipython_cell_code_name(code_name):
+ # type: (str) -> bool
+ return bool(re.match(r"(|)$", code_name))
+
+
+def is_ipython_cell_filename(filename):
+ # type: (str) -> bool
+ return bool(re.search(r" bool
+ return (
+ is_ipython_cell_filename(code_obj.co_filename) and
+ is_ipython_cell_code_name(code_obj.co_name)
+ )
+
+
+def find_node_ipython(frame, lasti, stmts, source):
+ # type: (types.FrameType, int, Set[EnhancedAST], Source) -> Tuple[Optional[Any], Optional[Any]]
+ node = decorator = None
+ for stmt in stmts:
+ tree = _extract_ipython_statement(stmt)
+ try:
+ node_finder = NodeFinder(frame, stmts, tree, lasti, source)
+ if (node or decorator) and (node_finder.result or node_finder.decorator):
+ # Found potential nodes in separate statements,
+ # cannot resolve ambiguity, give up here
+ return None, None
+
+ node = node_finder.result
+ decorator = node_finder.decorator
+ except Exception:
+ pass
+ return decorator, node
+
+
+def attr_names_match(attr, argval):
+ # type: (str, str) -> bool
+ """
+ Checks that the user-visible attr (from ast) can correspond to
+ the argval in the bytecode, i.e. the real attribute fetched internally,
+ which may be mangled for private attributes.
+ """
+ if attr == argval:
+ return True
+ if not attr.startswith("__"):
+ return False
+ return bool(re.match(r"^_\w+%s$" % attr, argval))
+
+
+def node_linenos(node):
+ # type: (ast.AST) -> Iterator[int]
+ if hasattr(node, "lineno"):
+ linenos = [] # type: Sequence[int]
+ if hasattr(node, "end_lineno") and isinstance(node, ast.expr):
+ assert node.end_lineno is not None # type: ignore[attr-defined]
+ linenos = range(node.lineno, node.end_lineno + 1) # type: ignore[attr-defined]
+ else:
+ linenos = [node.lineno] # type: ignore[attr-defined]
+ for lineno in linenos:
+ yield lineno
+
+
+if sys.version_info >= (3, 11):
+ from ._position_node_finder import PositionNodeFinder as NodeFinder
+else:
+ NodeFinder = SentinelNodeFinder
+
diff --git a/venv/lib/python3.10/site-packages/executing/py.typed b/venv/lib/python3.10/site-packages/executing/py.typed
new file mode 100644
index 000000000..e69de29bb
diff --git a/venv/lib/python3.10/site-packages/executing/version.py b/venv/lib/python3.10/site-packages/executing/version.py
new file mode 100644
index 000000000..e916218b9
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/executing/version.py
@@ -0,0 +1 @@
+__version__ = '1.2.0'
\ No newline at end of file
diff --git a/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/INSTALLER
new file mode 100644
index 000000000..a1b589e38
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/METADATA b/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/METADATA
new file mode 100644
index 000000000..f8bcc58eb
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/METADATA
@@ -0,0 +1,56 @@
+Metadata-Version: 2.1
+Name: filelock
+Version: 3.13.1
+Summary: A platform independent file lock.
+Project-URL: Documentation, https://py-filelock.readthedocs.io
+Project-URL: Homepage, https://github.com/tox-dev/py-filelock
+Project-URL: Source, https://github.com/tox-dev/py-filelock
+Project-URL: Tracker, https://github.com/tox-dev/py-filelock/issues
+Maintainer-email: Bernát Gábor
+License-Expression: Unlicense
+License-File: LICENSE
+Keywords: application,cache,directory,log,user
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: The Unlicense (Unlicense)
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Topic :: Internet
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: System
+Requires-Python: >=3.8
+Provides-Extra: docs
+Requires-Dist: furo>=2023.9.10; extra == 'docs'
+Requires-Dist: sphinx-autodoc-typehints!=1.23.4,>=1.24; extra == 'docs'
+Requires-Dist: sphinx>=7.2.6; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: covdefaults>=2.3; extra == 'testing'
+Requires-Dist: coverage>=7.3.2; extra == 'testing'
+Requires-Dist: diff-cover>=8; extra == 'testing'
+Requires-Dist: pytest-cov>=4.1; extra == 'testing'
+Requires-Dist: pytest-mock>=3.12; extra == 'testing'
+Requires-Dist: pytest-timeout>=2.2; extra == 'testing'
+Requires-Dist: pytest>=7.4.3; extra == 'testing'
+Provides-Extra: typing
+Requires-Dist: typing-extensions>=4.8; python_version < '3.11' and extra == 'typing'
+Description-Content-Type: text/markdown
+
+# filelock
+
+[](https://pypi.org/project/filelock/)
+[](https://pypi.org/project/filelock/)
+[](https://py-filelock.readthedocs.io/en/latest/?badge=latest)
+[](https://github.com/psf/black)
+[](https://pepy.tech/project/filelock)
+[](https://github.com/tox-dev/py-filelock/actions/workflows/check.yml)
+
+For more information checkout the [official documentation](https://py-filelock.readthedocs.io/en/latest/index.html).
diff --git a/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/RECORD b/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/RECORD
new file mode 100644
index 000000000..49200d868
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/RECORD
@@ -0,0 +1,22 @@
+filelock-3.13.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+filelock-3.13.1.dist-info/METADATA,sha256=gi7LyG-dEuOBZC32wie-OOG0OkPZHABsn9rXvxuQlcA,2784
+filelock-3.13.1.dist-info/RECORD,,
+filelock-3.13.1.dist-info/WHEEL,sha256=9QBuHhg6FNW7lppboF2vKVbCGTVzsFykgRQjjlajrhA,87
+filelock-3.13.1.dist-info/licenses/LICENSE,sha256=iNm062BXnBkew5HKBMFhMFctfu3EqG2qWL8oxuFMm80,1210
+filelock/__init__.py,sha256=wAVZ_9_-3Y14xzzupRk5BTTRewFJekR2vf9oIx4M750,1213
+filelock/__pycache__/__init__.cpython-310.pyc,,
+filelock/__pycache__/_api.cpython-310.pyc,,
+filelock/__pycache__/_error.cpython-310.pyc,,
+filelock/__pycache__/_soft.cpython-310.pyc,,
+filelock/__pycache__/_unix.cpython-310.pyc,,
+filelock/__pycache__/_util.cpython-310.pyc,,
+filelock/__pycache__/_windows.cpython-310.pyc,,
+filelock/__pycache__/version.cpython-310.pyc,,
+filelock/_api.py,sha256=UsVWPEOOgFH1pR_6WMk2b5hWZ7nWhUPT5GZX9WuYaC8,11860
+filelock/_error.py,sha256=-5jMcjTu60YAvAO1UbqDD1GIEjVkwr8xCFwDBtMeYDg,787
+filelock/_soft.py,sha256=haqtc_TB_KJbYv2a8iuEAclKuM4fMG1vTcp28sK919c,1711
+filelock/_unix.py,sha256=ViG38PgJsIhT3xaArugvw0TPP6VWoP2VJj7FEIWypkg,2157
+filelock/_util.py,sha256=dBDlIj1dHL_juXX0Qqq6bZtyE53YZTN8GFhtyTV043o,1708
+filelock/_windows.py,sha256=eMKL8dZKrgekf5VYVGR14an29JGEInRtUO8ui9ABywg,2177
+filelock/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+filelock/version.py,sha256=fmajg3X8ZdOn-UpUewARwK5cfYf4wP4Xa0DcHjigFYo,413
diff --git a/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/WHEEL b/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/WHEEL
new file mode 100644
index 000000000..ba1a8af28
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/WHEEL
@@ -0,0 +1,4 @@
+Wheel-Version: 1.0
+Generator: hatchling 1.18.0
+Root-Is-Purelib: true
+Tag: py3-none-any
diff --git a/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/licenses/LICENSE b/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/licenses/LICENSE
new file mode 100644
index 000000000..cf1ab25da
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock-3.13.1.dist-info/licenses/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+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 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.
+
+For more information, please refer to
diff --git a/venv/lib/python3.10/site-packages/filelock/__init__.py b/venv/lib/python3.10/site-packages/filelock/__init__.py
new file mode 100644
index 000000000..4cf3b507d
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock/__init__.py
@@ -0,0 +1,51 @@
+"""
+A platform independent file lock that supports the with-statement.
+
+.. autodata:: filelock.__version__
+ :no-value:
+
+"""
+from __future__ import annotations
+
+import sys
+import warnings
+from typing import TYPE_CHECKING
+
+from ._api import AcquireReturnProxy, BaseFileLock
+from ._error import Timeout
+from ._soft import SoftFileLock
+from ._unix import UnixFileLock, has_fcntl
+from ._windows import WindowsFileLock
+from .version import version
+
+#: version of the project as a string
+__version__: str = version
+
+
+if sys.platform == "win32": # pragma: win32 cover
+ _FileLock: type[BaseFileLock] = WindowsFileLock
+else: # pragma: win32 no cover # noqa: PLR5501
+ if has_fcntl:
+ _FileLock: type[BaseFileLock] = UnixFileLock
+ else:
+ _FileLock = SoftFileLock
+ if warnings is not None:
+ warnings.warn("only soft file lock is available", stacklevel=2)
+
+if TYPE_CHECKING:
+ FileLock = SoftFileLock
+else:
+ #: Alias for the lock, which should be used for the current platform.
+ FileLock = _FileLock
+
+
+__all__ = [
+ "__version__",
+ "FileLock",
+ "SoftFileLock",
+ "Timeout",
+ "UnixFileLock",
+ "WindowsFileLock",
+ "BaseFileLock",
+ "AcquireReturnProxy",
+]
diff --git a/venv/lib/python3.10/site-packages/filelock/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/filelock/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 000000000..971cd6f70
Binary files /dev/null and b/venv/lib/python3.10/site-packages/filelock/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/filelock/__pycache__/_api.cpython-310.pyc b/venv/lib/python3.10/site-packages/filelock/__pycache__/_api.cpython-310.pyc
new file mode 100644
index 000000000..702f07a5e
Binary files /dev/null and b/venv/lib/python3.10/site-packages/filelock/__pycache__/_api.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/filelock/__pycache__/_error.cpython-310.pyc b/venv/lib/python3.10/site-packages/filelock/__pycache__/_error.cpython-310.pyc
new file mode 100644
index 000000000..acd256192
Binary files /dev/null and b/venv/lib/python3.10/site-packages/filelock/__pycache__/_error.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/filelock/__pycache__/_soft.cpython-310.pyc b/venv/lib/python3.10/site-packages/filelock/__pycache__/_soft.cpython-310.pyc
new file mode 100644
index 000000000..f6e02e831
Binary files /dev/null and b/venv/lib/python3.10/site-packages/filelock/__pycache__/_soft.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/filelock/__pycache__/_unix.cpython-310.pyc b/venv/lib/python3.10/site-packages/filelock/__pycache__/_unix.cpython-310.pyc
new file mode 100644
index 000000000..06bedcfd9
Binary files /dev/null and b/venv/lib/python3.10/site-packages/filelock/__pycache__/_unix.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/filelock/__pycache__/_util.cpython-310.pyc b/venv/lib/python3.10/site-packages/filelock/__pycache__/_util.cpython-310.pyc
new file mode 100644
index 000000000..fab983bd7
Binary files /dev/null and b/venv/lib/python3.10/site-packages/filelock/__pycache__/_util.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/filelock/__pycache__/_windows.cpython-310.pyc b/venv/lib/python3.10/site-packages/filelock/__pycache__/_windows.cpython-310.pyc
new file mode 100644
index 000000000..e55ef8df3
Binary files /dev/null and b/venv/lib/python3.10/site-packages/filelock/__pycache__/_windows.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/filelock/__pycache__/version.cpython-310.pyc b/venv/lib/python3.10/site-packages/filelock/__pycache__/version.cpython-310.pyc
new file mode 100644
index 000000000..bafa2406c
Binary files /dev/null and b/venv/lib/python3.10/site-packages/filelock/__pycache__/version.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/filelock/_api.py b/venv/lib/python3.10/site-packages/filelock/_api.py
new file mode 100644
index 000000000..2e9cdbad2
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock/_api.py
@@ -0,0 +1,323 @@
+from __future__ import annotations
+
+import contextlib
+import logging
+import os
+import time
+import warnings
+from abc import ABC, abstractmethod
+from dataclasses import dataclass
+from threading import local
+from typing import TYPE_CHECKING, Any, ClassVar
+from weakref import WeakValueDictionary
+
+from ._error import Timeout
+
+if TYPE_CHECKING:
+ import sys
+ from types import TracebackType
+
+ if sys.version_info >= (3, 11): # pragma: no cover (py311+)
+ from typing import Self
+ else: # pragma: no cover ( None:
+ self.lock = lock
+
+ def __enter__(self) -> BaseFileLock:
+ return self.lock
+
+ def __exit__(
+ self,
+ exc_type: type[BaseException] | None,
+ exc_value: BaseException | None,
+ traceback: TracebackType | None,
+ ) -> None:
+ self.lock.release()
+
+
+@dataclass
+class FileLockContext:
+ """A dataclass which holds the context for a ``BaseFileLock`` object."""
+
+ # The context is held in a separate class to allow optional use of thread local storage via the
+ # ThreadLocalFileContext class.
+
+ #: The path to the lock file.
+ lock_file: str
+
+ #: The default timeout value.
+ timeout: float
+
+ #: The mode for the lock files
+ mode: int
+
+ #: The file descriptor for the *_lock_file* as it is returned by the os.open() function, not None when lock held
+ lock_file_fd: int | None = None
+
+ #: The lock counter is used for implementing the nested locking mechanism.
+ lock_counter: int = 0 # When the lock is acquired is increased and the lock is only released, when this value is 0
+
+
+class ThreadLocalFileContext(FileLockContext, local):
+ """A thread local version of the ``FileLockContext`` class."""
+
+
+class BaseFileLock(ABC, contextlib.ContextDecorator):
+ """Abstract base class for a file lock object."""
+
+ _instances: ClassVar[WeakValueDictionary[str, BaseFileLock]] = WeakValueDictionary()
+
+ def __new__( # noqa: PLR0913
+ cls,
+ lock_file: str | os.PathLike[str],
+ timeout: float = -1, # noqa: ARG003
+ mode: int = 0o644, # noqa: ARG003
+ thread_local: bool = True, # noqa: ARG003, FBT001, FBT002
+ *,
+ is_singleton: bool = False,
+ **kwargs: dict[str, Any], # capture remaining kwargs for subclasses # noqa: ARG003
+ ) -> Self:
+ """Create a new lock object or if specified return the singleton instance for the lock file."""
+ if not is_singleton:
+ return super().__new__(cls)
+
+ instance = cls._instances.get(str(lock_file))
+ if not instance:
+ instance = super().__new__(cls)
+ cls._instances[str(lock_file)] = instance
+
+ return instance # type: ignore[return-value] # https://github.com/python/mypy/issues/15322
+
+ def __init__( # noqa: PLR0913
+ self,
+ lock_file: str | os.PathLike[str],
+ timeout: float = -1,
+ mode: int = 0o644,
+ thread_local: bool = True, # noqa: FBT001, FBT002
+ *,
+ is_singleton: bool = False,
+ ) -> None:
+ """
+ Create a new lock object.
+
+ :param lock_file: path to the file
+ :param timeout: default timeout when acquiring the lock, in seconds. It will be used as fallback value in \
+ the acquire method, if no timeout value (``None``) is given. If you want to disable the timeout, set it \
+ to a negative value. A timeout of 0 means, that there is exactly one attempt to acquire the file lock.
+ :param mode: file permissions for the lockfile
+ :param thread_local: Whether this object's internal context should be thread local or not. If this is set to \
+ ``False`` then the lock will be reentrant across threads.
+ :param is_singleton: If this is set to ``True`` then only one instance of this class will be created \
+ per lock file. This is useful if you want to use the lock object for reentrant locking without needing \
+ to pass the same object around.
+ """
+ self._is_thread_local = thread_local
+ self._is_singleton = is_singleton
+
+ # Create the context. Note that external code should not work with the context directly and should instead use
+ # properties of this class.
+ kwargs: dict[str, Any] = {
+ "lock_file": os.fspath(lock_file),
+ "timeout": timeout,
+ "mode": mode,
+ }
+ self._context: FileLockContext = (ThreadLocalFileContext if thread_local else FileLockContext)(**kwargs)
+
+ def is_thread_local(self) -> bool:
+ """:return: a flag indicating if this lock is thread local or not"""
+ return self._is_thread_local
+
+ @property
+ def is_singleton(self) -> bool:
+ """:return: a flag indicating if this lock is singleton or not"""
+ return self._is_singleton
+
+ @property
+ def lock_file(self) -> str:
+ """:return: path to the lock file"""
+ return self._context.lock_file
+
+ @property
+ def timeout(self) -> float:
+ """
+ :return: the default timeout value, in seconds
+
+ .. versionadded:: 2.0.0
+ """
+ return self._context.timeout
+
+ @timeout.setter
+ def timeout(self, value: float | str) -> None:
+ """
+ Change the default timeout value.
+
+ :param value: the new value, in seconds
+ """
+ self._context.timeout = float(value)
+
+ @abstractmethod
+ def _acquire(self) -> None:
+ """If the file lock could be acquired, self._context.lock_file_fd holds the file descriptor of the lock file."""
+ raise NotImplementedError
+
+ @abstractmethod
+ def _release(self) -> None:
+ """Releases the lock and sets self._context.lock_file_fd to None."""
+ raise NotImplementedError
+
+ @property
+ def is_locked(self) -> bool:
+ """
+
+ :return: A boolean indicating if the lock file is holding the lock currently.
+
+ .. versionchanged:: 2.0.0
+
+ This was previously a method and is now a property.
+ """
+ return self._context.lock_file_fd is not None
+
+ @property
+ def lock_counter(self) -> int:
+ """:return: The number of times this lock has been acquired (but not yet released)."""
+ return self._context.lock_counter
+
+ def acquire(
+ self,
+ timeout: float | None = None,
+ poll_interval: float = 0.05,
+ *,
+ poll_intervall: float | None = None,
+ blocking: bool = True,
+ ) -> AcquireReturnProxy:
+ """
+ Try to acquire the file lock.
+
+ :param timeout: maximum wait time for acquiring the lock, ``None`` means use the default :attr:`~timeout` is and
+ if ``timeout < 0``, there is no timeout and this method will block until the lock could be acquired
+ :param poll_interval: interval of trying to acquire the lock file
+ :param poll_intervall: deprecated, kept for backwards compatibility, use ``poll_interval`` instead
+ :param blocking: defaults to True. If False, function will return immediately if it cannot obtain a lock on the
+ first attempt. Otherwise, this method will block until the timeout expires or the lock is acquired.
+ :raises Timeout: if fails to acquire lock within the timeout period
+ :return: a context object that will unlock the file when the context is exited
+
+ .. code-block:: python
+
+ # You can use this method in the context manager (recommended)
+ with lock.acquire():
+ pass
+
+ # Or use an equivalent try-finally construct:
+ lock.acquire()
+ try:
+ pass
+ finally:
+ lock.release()
+
+ .. versionchanged:: 2.0.0
+
+ This method returns now a *proxy* object instead of *self*,
+ so that it can be used in a with statement without side effects.
+
+ """
+ # Use the default timeout, if no timeout is provided.
+ if timeout is None:
+ timeout = self._context.timeout
+
+ if poll_intervall is not None:
+ msg = "use poll_interval instead of poll_intervall"
+ warnings.warn(msg, DeprecationWarning, stacklevel=2)
+ poll_interval = poll_intervall
+
+ # Increment the number right at the beginning. We can still undo it, if something fails.
+ self._context.lock_counter += 1
+
+ lock_id = id(self)
+ lock_filename = self.lock_file
+ start_time = time.perf_counter()
+ try:
+ while True:
+ if not self.is_locked:
+ _LOGGER.debug("Attempting to acquire lock %s on %s", lock_id, lock_filename)
+ self._acquire()
+ if self.is_locked:
+ _LOGGER.debug("Lock %s acquired on %s", lock_id, lock_filename)
+ break
+ if blocking is False:
+ _LOGGER.debug("Failed to immediately acquire lock %s on %s", lock_id, lock_filename)
+ raise Timeout(lock_filename) # noqa: TRY301
+ if 0 <= timeout < time.perf_counter() - start_time:
+ _LOGGER.debug("Timeout on acquiring lock %s on %s", lock_id, lock_filename)
+ raise Timeout(lock_filename) # noqa: TRY301
+ msg = "Lock %s not acquired on %s, waiting %s seconds ..."
+ _LOGGER.debug(msg, lock_id, lock_filename, poll_interval)
+ time.sleep(poll_interval)
+ except BaseException: # Something did go wrong, so decrement the counter.
+ self._context.lock_counter = max(0, self._context.lock_counter - 1)
+ raise
+ return AcquireReturnProxy(lock=self)
+
+ def release(self, force: bool = False) -> None: # noqa: FBT001, FBT002
+ """
+ Releases the file lock. Please note, that the lock is only completely released, if the lock counter is 0. Also
+ note, that the lock file itself is not automatically deleted.
+
+ :param force: If true, the lock counter is ignored and the lock is released in every case/
+ """
+ if self.is_locked:
+ self._context.lock_counter -= 1
+
+ if self._context.lock_counter == 0 or force:
+ lock_id, lock_filename = id(self), self.lock_file
+
+ _LOGGER.debug("Attempting to release lock %s on %s", lock_id, lock_filename)
+ self._release()
+ self._context.lock_counter = 0
+ _LOGGER.debug("Lock %s released on %s", lock_id, lock_filename)
+
+ def __enter__(self) -> Self:
+ """
+ Acquire the lock.
+
+ :return: the lock object
+ """
+ self.acquire()
+ return self
+
+ def __exit__(
+ self,
+ exc_type: type[BaseException] | None,
+ exc_value: BaseException | None,
+ traceback: TracebackType | None,
+ ) -> None:
+ """
+ Release the lock.
+
+ :param exc_type: the exception type if raised
+ :param exc_value: the exception value if raised
+ :param traceback: the exception traceback if raised
+ """
+ self.release()
+
+ def __del__(self) -> None:
+ """Called when the lock object is deleted."""
+ self.release(force=True)
+
+
+__all__ = [
+ "BaseFileLock",
+ "AcquireReturnProxy",
+]
diff --git a/venv/lib/python3.10/site-packages/filelock/_error.py b/venv/lib/python3.10/site-packages/filelock/_error.py
new file mode 100644
index 000000000..f7ff08c0f
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock/_error.py
@@ -0,0 +1,30 @@
+from __future__ import annotations
+
+from typing import Any
+
+
+class Timeout(TimeoutError): # noqa: N818
+ """Raised when the lock could not be acquired in *timeout* seconds."""
+
+ def __init__(self, lock_file: str) -> None:
+ super().__init__()
+ self._lock_file = lock_file
+
+ def __reduce__(self) -> str | tuple[Any, ...]:
+ return self.__class__, (self._lock_file,) # Properly pickle the exception
+
+ def __str__(self) -> str:
+ return f"The file lock '{self._lock_file}' could not be acquired."
+
+ def __repr__(self) -> str:
+ return f"{self.__class__.__name__}({self.lock_file!r})"
+
+ @property
+ def lock_file(self) -> str:
+ """:return: The path of the file lock."""
+ return self._lock_file
+
+
+__all__ = [
+ "Timeout",
+]
diff --git a/venv/lib/python3.10/site-packages/filelock/_soft.py b/venv/lib/python3.10/site-packages/filelock/_soft.py
new file mode 100644
index 000000000..28c67f74c
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock/_soft.py
@@ -0,0 +1,47 @@
+from __future__ import annotations
+
+import os
+import sys
+from contextlib import suppress
+from errno import EACCES, EEXIST
+from pathlib import Path
+
+from ._api import BaseFileLock
+from ._util import ensure_directory_exists, raise_on_not_writable_file
+
+
+class SoftFileLock(BaseFileLock):
+ """Simply watches the existence of the lock file."""
+
+ def _acquire(self) -> None:
+ raise_on_not_writable_file(self.lock_file)
+ ensure_directory_exists(self.lock_file)
+ # first check for exists and read-only mode as the open will mask this case as EEXIST
+ flags = (
+ os.O_WRONLY # open for writing only
+ | os.O_CREAT
+ | os.O_EXCL # together with above raise EEXIST if the file specified by filename exists
+ | os.O_TRUNC # truncate the file to zero byte
+ )
+ try:
+ file_handler = os.open(self.lock_file, flags, self._context.mode)
+ except OSError as exception: # re-raise unless expected exception
+ if not (
+ exception.errno == EEXIST # lock already exist
+ or (exception.errno == EACCES and sys.platform == "win32") # has no access to this lock
+ ): # pragma: win32 no cover
+ raise
+ else:
+ self._context.lock_file_fd = file_handler
+
+ def _release(self) -> None:
+ assert self._context.lock_file_fd is not None # noqa: S101
+ os.close(self._context.lock_file_fd) # the lock file is definitely not None
+ self._context.lock_file_fd = None
+ with suppress(OSError): # the file is already deleted and that's what we want
+ Path(self.lock_file).unlink()
+
+
+__all__ = [
+ "SoftFileLock",
+]
diff --git a/venv/lib/python3.10/site-packages/filelock/_unix.py b/venv/lib/python3.10/site-packages/filelock/_unix.py
new file mode 100644
index 000000000..93ce3be58
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock/_unix.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+import os
+import sys
+from contextlib import suppress
+from errno import ENOSYS
+from typing import cast
+
+from ._api import BaseFileLock
+from ._util import ensure_directory_exists
+
+#: a flag to indicate if the fcntl API is available
+has_fcntl = False
+if sys.platform == "win32": # pragma: win32 cover
+
+ class UnixFileLock(BaseFileLock):
+ """Uses the :func:`fcntl.flock` to hard lock the lock file on unix systems."""
+
+ def _acquire(self) -> None:
+ raise NotImplementedError
+
+ def _release(self) -> None:
+ raise NotImplementedError
+
+else: # pragma: win32 no cover
+ try:
+ import fcntl
+ except ImportError:
+ pass
+ else:
+ has_fcntl = True
+
+ class UnixFileLock(BaseFileLock):
+ """Uses the :func:`fcntl.flock` to hard lock the lock file on unix systems."""
+
+ def _acquire(self) -> None:
+ ensure_directory_exists(self.lock_file)
+ open_flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC
+ fd = os.open(self.lock_file, open_flags, self._context.mode)
+ with suppress(PermissionError): # This locked is not owned by this UID
+ os.fchmod(fd, self._context.mode)
+ try:
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ except OSError as exception:
+ os.close(fd)
+ if exception.errno == ENOSYS: # NotImplemented error
+ msg = "FileSystem does not appear to support flock; user SoftFileLock instead"
+ raise NotImplementedError(msg) from exception
+ else:
+ self._context.lock_file_fd = fd
+
+ def _release(self) -> None:
+ # Do not remove the lockfile:
+ # https://github.com/tox-dev/py-filelock/issues/31
+ # https://stackoverflow.com/questions/17708885/flock-removing-locked-file-without-race-condition
+ fd = cast(int, self._context.lock_file_fd)
+ self._context.lock_file_fd = None
+ fcntl.flock(fd, fcntl.LOCK_UN)
+ os.close(fd)
+
+
+__all__ = [
+ "has_fcntl",
+ "UnixFileLock",
+]
diff --git a/venv/lib/python3.10/site-packages/filelock/_util.py b/venv/lib/python3.10/site-packages/filelock/_util.py
new file mode 100644
index 000000000..543c13946
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock/_util.py
@@ -0,0 +1,47 @@
+from __future__ import annotations
+
+import os
+import stat
+import sys
+from errno import EACCES, EISDIR
+from pathlib import Path
+
+
+def raise_on_not_writable_file(filename: str) -> None:
+ """
+ Raise an exception if attempting to open the file for writing would fail.
+ This is done so files that will never be writable can be separated from
+ files that are writable but currently locked
+ :param filename: file to check
+ :raises OSError: as if the file was opened for writing.
+ """
+ try: # use stat to do exists + can write to check without race condition
+ file_stat = os.stat(filename) # noqa: PTH116
+ except OSError:
+ return # swallow does not exist or other errors
+
+ if file_stat.st_mtime != 0: # if os.stat returns but modification is zero that's an invalid os.stat - ignore it
+ if not (file_stat.st_mode & stat.S_IWUSR):
+ raise PermissionError(EACCES, "Permission denied", filename)
+
+ if stat.S_ISDIR(file_stat.st_mode):
+ if sys.platform == "win32": # pragma: win32 cover
+ # On Windows, this is PermissionError
+ raise PermissionError(EACCES, "Permission denied", filename)
+ else: # pragma: win32 no cover # noqa: RET506
+ # On linux / macOS, this is IsADirectoryError
+ raise IsADirectoryError(EISDIR, "Is a directory", filename)
+
+
+def ensure_directory_exists(filename: Path | str) -> None:
+ """
+ Ensure the directory containing the file exists (create it if necessary)
+ :param filename: file.
+ """
+ Path(filename).parent.mkdir(parents=True, exist_ok=True)
+
+
+__all__ = [
+ "raise_on_not_writable_file",
+ "ensure_directory_exists",
+]
diff --git a/venv/lib/python3.10/site-packages/filelock/_windows.py b/venv/lib/python3.10/site-packages/filelock/_windows.py
new file mode 100644
index 000000000..8db55dcba
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock/_windows.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+import os
+import sys
+from contextlib import suppress
+from errno import EACCES
+from pathlib import Path
+from typing import cast
+
+from ._api import BaseFileLock
+from ._util import ensure_directory_exists, raise_on_not_writable_file
+
+if sys.platform == "win32": # pragma: win32 cover
+ import msvcrt
+
+ class WindowsFileLock(BaseFileLock):
+ """Uses the :func:`msvcrt.locking` function to hard lock the lock file on Windows systems."""
+
+ def _acquire(self) -> None:
+ raise_on_not_writable_file(self.lock_file)
+ ensure_directory_exists(self.lock_file)
+ flags = (
+ os.O_RDWR # open for read and write
+ | os.O_CREAT # create file if not exists
+ | os.O_TRUNC # truncate file if not empty
+ )
+ try:
+ fd = os.open(self.lock_file, flags, self._context.mode)
+ except OSError as exception:
+ if exception.errno != EACCES: # has no access to this lock
+ raise
+ else:
+ try:
+ msvcrt.locking(fd, msvcrt.LK_NBLCK, 1)
+ except OSError as exception:
+ os.close(fd) # close file first
+ if exception.errno != EACCES: # file is already locked
+ raise
+ else:
+ self._context.lock_file_fd = fd
+
+ def _release(self) -> None:
+ fd = cast(int, self._context.lock_file_fd)
+ self._context.lock_file_fd = None
+ msvcrt.locking(fd, msvcrt.LK_UNLCK, 1)
+ os.close(fd)
+
+ with suppress(OSError): # Probably another instance of the application hat acquired the file lock.
+ Path(self.lock_file).unlink()
+
+else: # pragma: win32 no cover
+
+ class WindowsFileLock(BaseFileLock):
+ """Uses the :func:`msvcrt.locking` function to hard lock the lock file on Windows systems."""
+
+ def _acquire(self) -> None:
+ raise NotImplementedError
+
+ def _release(self) -> None:
+ raise NotImplementedError
+
+
+__all__ = [
+ "WindowsFileLock",
+]
diff --git a/venv/lib/python3.10/site-packages/filelock/py.typed b/venv/lib/python3.10/site-packages/filelock/py.typed
new file mode 100644
index 000000000..e69de29bb
diff --git a/venv/lib/python3.10/site-packages/filelock/version.py b/venv/lib/python3.10/site-packages/filelock/version.py
new file mode 100644
index 000000000..cc9fc1550
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/filelock/version.py
@@ -0,0 +1,16 @@
+# file generated by setuptools_scm
+# don't change, don't track in version control
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from typing import Tuple, Union
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
+else:
+ VERSION_TUPLE = object
+
+version: str
+__version__: str
+__version_tuple__: VERSION_TUPLE
+version_tuple: VERSION_TUPLE
+
+__version__ = version = '3.13.1'
+__version_tuple__ = version_tuple = (3, 13, 1)
diff --git a/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/AUTHORS b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/AUTHORS
new file mode 100644
index 000000000..42a5c227b
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/AUTHORS
@@ -0,0 +1,51 @@
+Original Authors
+----------------
+* Armin Rigo
+* Christian Tismer
+
+Contributors
+------------
+* Al Stone
+* Alexander Schmidt
+* Alexey Borzenkov
+* Andreas Schwab
+* Armin Ronacher
+* Bin Wang
+* Bob Ippolito
+* ChangBo Guo
+* Christoph Gohlke
+* Denis Bilenko
+* Dirk Mueller
+* Donovan Preston
+* Fantix King
+* Floris Bruynooghe
+* Fredrik Fornwall
+* Gerd Woetzel
+* Giel van Schijndel
+* Gökhan Karabulut
+* Gustavo Niemeyer
+* Guy Rozendorn
+* Hye-Shik Chang
+* Jared Kuolt
+* Jason Madden
+* Josh Snyder
+* Kyle Ambroff
+* Laszlo Boszormenyi
+* Mao Han
+* Marc Abramowitz
+* Marc Schlaich
+* Marcin Bachry
+* Matt Madison
+* Matt Turner
+* Michael Ellerman
+* Michael Matz
+* Ralf Schmitt
+* Robie Basak
+* Ronny Pfannschmidt
+* Samual M. Rushing
+* Tony Bowles
+* Tony Breeds
+* Trevor Bowen
+* Tulio Magno Quites Machado Filho
+* Ulrich Weigand
+* Victor Stinner
diff --git a/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/INSTALLER
new file mode 100644
index 000000000..a1b589e38
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/LICENSE b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/LICENSE
new file mode 100644
index 000000000..b73a4a10c
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/LICENSE
@@ -0,0 +1,30 @@
+The following files are derived from Stackless Python and are subject to the
+same license as Stackless Python:
+
+ src/greenlet/slp_platformselect.h
+ files in src/greenlet/platform/ directory
+
+See LICENSE.PSF and http://www.stackless.com/ for details.
+
+Unless otherwise noted, the files in greenlet have been released under the
+following MIT license:
+
+Copyright (c) Armin Rigo, Christian Tismer and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/LICENSE.PSF b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/LICENSE.PSF
new file mode 100644
index 000000000..d3b509a2b
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/LICENSE.PSF
@@ -0,0 +1,47 @@
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+2011 Python Software Foundation; All Rights Reserved" are retained in Python
+alone or in any derivative version prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee. This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
diff --git a/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/METADATA b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/METADATA
new file mode 100644
index 000000000..141effe0a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/METADATA
@@ -0,0 +1,100 @@
+Metadata-Version: 2.1
+Name: greenlet
+Version: 3.0.1
+Summary: Lightweight in-process concurrent programming
+Home-page: https://greenlet.readthedocs.io/
+Author: Alexey Borzenkov
+Author-email: snaury@gmail.com
+Maintainer: Jason Madden
+Maintainer-email: jason@seecoresoftware.com
+License: MIT License
+Project-URL: Bug Tracker, https://github.com/python-greenlet/greenlet/issues
+Project-URL: Source Code, https://github.com/python-greenlet/greenlet/
+Project-URL: Documentation, https://greenlet.readthedocs.io/
+Keywords: greenlet coroutine concurrency threads cooperative
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Natural Language :: English
+Classifier: Programming Language :: C
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Operating System :: OS Independent
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=3.7
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+License-File: LICENSE.PSF
+License-File: AUTHORS
+Provides-Extra: docs
+Requires-Dist: Sphinx ; extra == 'docs'
+Provides-Extra: test
+Requires-Dist: objgraph ; extra == 'test'
+Requires-Dist: psutil ; extra == 'test'
+
+.. This file is included into docs/history.rst
+
+
+Greenlets are lightweight coroutines for in-process concurrent
+programming.
+
+The "greenlet" package is a spin-off of `Stackless`_, a version of
+CPython that supports micro-threads called "tasklets". Tasklets run
+pseudo-concurrently (typically in a single or a few OS-level threads)
+and are synchronized with data exchanges on "channels".
+
+A "greenlet", on the other hand, is a still more primitive notion of
+micro-thread with no implicit scheduling; coroutines, in other words.
+This is useful when you want to control exactly when your code runs.
+You can build custom scheduled micro-threads on top of greenlet;
+however, it seems that greenlets are useful on their own as a way to
+make advanced control flow structures. For example, we can recreate
+generators; the difference with Python's own generators is that our
+generators can call nested functions and the nested functions can
+yield values too. (Additionally, you don't need a "yield" keyword. See
+the example in `test_generator.py
+`_).
+
+Greenlets are provided as a C extension module for the regular unmodified
+interpreter.
+
+.. _`Stackless`: http://www.stackless.com
+
+
+Who is using Greenlet?
+======================
+
+There are several libraries that use Greenlet as a more flexible
+alternative to Python's built in coroutine support:
+
+ - `Concurrence`_
+ - `Eventlet`_
+ - `Gevent`_
+
+.. _Concurrence: http://opensource.hyves.org/concurrence/
+.. _Eventlet: http://eventlet.net/
+.. _Gevent: http://www.gevent.org/
+
+Getting Greenlet
+================
+
+The easiest way to get Greenlet is to install it with pip::
+
+ pip install greenlet
+
+
+Source code archives and binary distributions are available on the
+python package index at https://pypi.org/project/greenlet
+
+The source code repository is hosted on github:
+https://github.com/python-greenlet/greenlet
+
+Documentation is available on readthedocs.org:
+https://greenlet.readthedocs.io
diff --git a/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/RECORD b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/RECORD
new file mode 100644
index 000000000..279ed5619
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/RECORD
@@ -0,0 +1,116 @@
+../../../include/site/python3.10/greenlet/greenlet.h,sha256=sz5pYRSQqedgOt2AMgxLZdTjO-qcr_JMvgiEJR9IAJ8,4755
+greenlet-3.0.1.dist-info/AUTHORS,sha256=swW28t2knVRxRkaEQNZtO7MP9Sgnompb7B6cNgJM8Gk,849
+greenlet-3.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+greenlet-3.0.1.dist-info/LICENSE,sha256=dpgx1uXfrywggC-sz_H6-0wgJd2PYlPfpH_K1Z1NCXk,1434
+greenlet-3.0.1.dist-info/LICENSE.PSF,sha256=5f88I8EQ5JTNfXNsEP2W1GJFe6_soxCEDbZScpjH1Gs,2424
+greenlet-3.0.1.dist-info/METADATA,sha256=CDw_jgaGKHnHtVv6N9MP6H4bU9IEIj3NCWKCC4pQmz4,3690
+greenlet-3.0.1.dist-info/RECORD,,
+greenlet-3.0.1.dist-info/WHEEL,sha256=_4914oG8cl8Mi1ov3DaAtJkpEq8_IcAjEGxbIw1B5b8,153
+greenlet-3.0.1.dist-info/top_level.txt,sha256=YSnRsCRoO61JGlP57o8iKL6rdLWDWuiyKD8ekpWUsDc,9
+greenlet/TBrokenGreenlet.cpp,sha256=YgKaHkQV6_dKBrgS0HKDSqZroskv0IwSZDo4bsiwz3w,1029
+greenlet/TExceptionState.cpp,sha256=Ctg2YfyEYNjOYbteRB_oIJa9lNGyC7N1F3h4XqqQdg8,1367
+greenlet/TGreenlet.cpp,sha256=tJjCH-hSwDEfcy0EmrOBG76wduj5KiU2IdkVhnEwnI8,20495
+greenlet/TGreenletGlobals.cpp,sha256=qLi1icS1UDSbefTkolz9TycEi_GOUblsEznMp0HFywQ,3268
+greenlet/TMainGreenlet.cpp,sha256=FvWtGJDKb64DLy0n-ddcTF6xJDwczPMKSm9mXSsHJKg,3365
+greenlet/TPythonState.cpp,sha256=b2vBapjZTf2WDv9bUSLy_807VmzaZP5rYO6JVY_LBOU,14677
+greenlet/TStackState.cpp,sha256=CMNZJ7YQqh66aM4IgLF7hGpG_vYMykxSPRCdbRxF7_s,6180
+greenlet/TThreadStateDestroy.cpp,sha256=EqZ-GjksrWNC20CY_P0yXN43wVRMYEh659SmRRqBaI4,7214
+greenlet/TUserGreenlet.cpp,sha256=IApI-Y761dTgESEHzkbbbH3zSgpI-F3aWFRhC6IiIPU,23341
+greenlet/__init__.py,sha256=GciQw3uTTef57lLNTMhim94R26oocYDPcDJMUPg0eNs,1723
+greenlet/__pycache__/__init__.cpython-310.pyc,,
+greenlet/_greenlet.cpython-310-x86_64-linux-gnu.so,sha256=HlXnJ-EJyzrPEOXlptdwYZMsTSjETFlhBVIDiprMqcU,1504864
+greenlet/greenlet.cpp,sha256=1ThdNjkCby1wU8pdiBx9ZJ1CaKbhYjsfrTKH_s2poBQ,48814
+greenlet/greenlet.h,sha256=sz5pYRSQqedgOt2AMgxLZdTjO-qcr_JMvgiEJR9IAJ8,4755
+greenlet/greenlet_allocator.hpp,sha256=kxyWW4Qdwlrc7ufgdb5vd6Y7jhauQ699Kod0mqiO1iM,1582
+greenlet/greenlet_compiler_compat.hpp,sha256=m7wvwrZqBoCQpDMTP-Z7whdXIES7e3AuXBgvPHSsfxg,4140
+greenlet/greenlet_cpython_add_pending.hpp,sha256=apAwIhGlgYrnYn03zWL6Sxy68kltDeb1e0QupZfb3DQ,6043
+greenlet/greenlet_cpython_compat.hpp,sha256=ZpN8gewZeOtd6T-mLidA7zteQ_P4vG8T1za_KPvCijg,3621
+greenlet/greenlet_exceptions.hpp,sha256=Dt8YdaQn8AK9nBfwU9rrDoMlR2Lw5aLTQV6ZAsHmfsw,3683
+greenlet/greenlet_greenlet.hpp,sha256=yyQ0Zi8dKvp-lbwHNQzcfpSvuAyDmKkwBlCZBZeY1w0,25764
+greenlet/greenlet_internal.hpp,sha256=ZXH5zemWCN8wH8zAqMUGycvz_3IulRL6Gf2hZA6CknE,2703
+greenlet/greenlet_refs.hpp,sha256=ECkHKV1CVamtzmWWGKXXMpw8lXLeIzastXM9tfqlsNI,33864
+greenlet/greenlet_slp_switch.hpp,sha256=kM1QHA2iV-gH4cFyN6lfIagHQxvJZjWOVJdIxRE3TlQ,3198
+greenlet/greenlet_thread_state.hpp,sha256=0UwJCNd86ifwM2yDd3QrNmHAECL-eNADHubwiB_XGA4,20614
+greenlet/greenlet_thread_state_dict_cleanup.hpp,sha256=tEN0rI1pZiEsdtr7Oda24gr52fGiHnYTLyM8Vme3Gns,3831
+greenlet/greenlet_thread_support.hpp,sha256=XUJ6ljWjf9OYyuOILiz8e_yHvT3fbaUiHdhiPNQUV4s,867
+greenlet/platform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+greenlet/platform/__pycache__/__init__.cpython-310.pyc,,
+greenlet/platform/setup_switch_x64_masm.cmd,sha256=ZpClUJeU0ujEPSTWNSepP0W2f9XiYQKA8QKSoVou8EU,143
+greenlet/platform/switch_aarch64_gcc.h,sha256=GKC0yWNXnbK2X--X6aguRCMj2Tg7hDU1Zkl3RljDvC8,4307
+greenlet/platform/switch_alpha_unix.h,sha256=Z-SvF8JQV3oxWT8JRbL9RFu4gRFxPdJ7cviM8YayMmw,671
+greenlet/platform/switch_amd64_unix.h,sha256=EcSFCBlodEBhqhKjcJqY_5Dn_jn7pKpkJlOvp7gFXLI,2748
+greenlet/platform/switch_arm32_gcc.h,sha256=Z3KkHszdgq6uU4YN3BxvKMG2AdDnovwCCNrqGWZ1Lyo,2479
+greenlet/platform/switch_arm32_ios.h,sha256=mm5_R9aXB92hyxzFRwB71M60H6AlvHjrpTrc72Pz3l8,1892
+greenlet/platform/switch_arm64_masm.asm,sha256=4kpTtfy7rfcr8j1CpJLAK21EtZpGDAJXWRU68HEy5A8,1245
+greenlet/platform/switch_arm64_masm.obj,sha256=DmLnIB_icoEHAz1naue_pJPTZgR9ElM7-Nmztr-o9_U,746
+greenlet/platform/switch_arm64_msvc.h,sha256=RqK5MHLmXI3Q-FQ7tm32KWnbDNZKnkJdq8CR89cz640,398
+greenlet/platform/switch_csky_gcc.h,sha256=kDikyiPpewP71KoBZQO_MukDTXTXBiC7x-hF0_2DL0w,1331
+greenlet/platform/switch_loongarch64_linux.h,sha256=7M-Dhc4Q8tRbJCJhalDLwU6S9Mx8MjmN1RbTDgIvQTM,779
+greenlet/platform/switch_m68k_gcc.h,sha256=VSa6NpZhvyyvF-Q58CTIWSpEDo4FKygOyTz00whctlw,928
+greenlet/platform/switch_mips_unix.h,sha256=E0tYsqc5anDY1BhenU1l8DW-nVHC_BElzLgJw3TGtPk,1426
+greenlet/platform/switch_ppc64_aix.h,sha256=_BL0iyRr3ZA5iPlr3uk9SJ5sNRWGYLrXcZ5z-CE9anE,3860
+greenlet/platform/switch_ppc64_linux.h,sha256=0rriT5XyxPb0GqsSSn_bP9iQsnjsPbBmu0yqo5goSyQ,3815
+greenlet/platform/switch_ppc_aix.h,sha256=pHA4slEjUFP3J3SYm1TAlNPhgb2G_PAtax5cO8BEe1A,2941
+greenlet/platform/switch_ppc_linux.h,sha256=YwrlKUzxlXuiKMQqr6MFAV1bPzWnmvk6X1AqJZEpOWU,2759
+greenlet/platform/switch_ppc_macosx.h,sha256=L8sB0c00V4G2_5cQCG3zX-23DKq3le_Dcj0sUDcACos,2624
+greenlet/platform/switch_ppc_unix.h,sha256=POy4bRBcH74Chfw4viFE9bVlZ-7BaNsFC0NnXr1L2tg,2652
+greenlet/platform/switch_riscv_unix.h,sha256=jX3vC_xZXiUho8tz4J6Ai8BNQB80yLn03fxkoMztVCU,740
+greenlet/platform/switch_s390_unix.h,sha256=RRlGu957ybmq95qNNY4Qw1mcaoT3eBnW5KbVwu48KX8,2763
+greenlet/platform/switch_sparc_sun_gcc.h,sha256=xZish9GsMHBienUbUMsX1-ZZ-as7hs36sVhYIE3ew8Y,2797
+greenlet/platform/switch_x32_unix.h,sha256=nM98PKtzTWc1lcM7TRMUZJzskVdR1C69U1UqZRWX0GE,1509
+greenlet/platform/switch_x64_masm.asm,sha256=nu6n2sWyXuXfpPx40d9YmLfHXUc1sHgeTvX1kUzuvEM,1841
+greenlet/platform/switch_x64_masm.obj,sha256=GNtTNxYdo7idFUYsQv-mrXWgyT5EJ93-9q90lN6svtQ,1078
+greenlet/platform/switch_x64_msvc.h,sha256=LIeasyKo_vHzspdMzMHbosRhrBfKI4BkQOh4qcTHyJw,1805
+greenlet/platform/switch_x86_msvc.h,sha256=TtGOwinbFfnn6clxMNkCz8i6OmgB6kVRrShoF5iT9to,12838
+greenlet/platform/switch_x86_unix.h,sha256=VplW9H0FF0cZHw1DhJdIUs5q6YLS4cwb2nYwjF83R1s,3059
+greenlet/slp_platformselect.h,sha256=JEnia_2HsTwdqvnnEsDxHQqalYvFJqx_CDsqvNUQYe8,3600
+greenlet/tests/__init__.py,sha256=F282jaIavKrhsYgHJEXtIQXKHdHpe9OJOPTK7R40JzI,9022
+greenlet/tests/__pycache__/__init__.cpython-310.pyc,,
+greenlet/tests/__pycache__/fail_clearing_run_switches.cpython-310.pyc,,
+greenlet/tests/__pycache__/fail_cpp_exception.cpython-310.pyc,,
+greenlet/tests/__pycache__/fail_initialstub_already_started.cpython-310.pyc,,
+greenlet/tests/__pycache__/fail_slp_switch.cpython-310.pyc,,
+greenlet/tests/__pycache__/fail_switch_three_greenlets.cpython-310.pyc,,
+greenlet/tests/__pycache__/fail_switch_three_greenlets2.cpython-310.pyc,,
+greenlet/tests/__pycache__/fail_switch_two_greenlets.cpython-310.pyc,,
+greenlet/tests/__pycache__/leakcheck.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_contextvars.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_cpp.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_extension_interface.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_gc.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_generator.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_generator_nested.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_greenlet.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_greenlet_trash.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_leaks.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_stack_saved.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_throw.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_tracing.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_version.cpython-310.pyc,,
+greenlet/tests/__pycache__/test_weakref.cpython-310.pyc,,
+greenlet/tests/_test_extension.c,sha256=vkeGA-6oeJcGILsD7oIrT1qZop2GaTOHXiNT7mcSl-0,5773
+greenlet/tests/_test_extension.cpython-310-x86_64-linux-gnu.so,sha256=GBJExwOb15y8CP47hakiiGHRQiwKWkAj1MRFhf2O5w4,36200
+greenlet/tests/_test_extension_cpp.cpp,sha256=ovRuyHHtrR5mVCnLOcGQiHtKqaEt5cyTP25t9k_YwU0,6152
+greenlet/tests/_test_extension_cpp.cpython-310-x86_64-linux-gnu.so,sha256=JS0pC63aCGk3mHsIKYpZeVQ3Gcicfjvab5axjJ19oYw,56176
+greenlet/tests/fail_clearing_run_switches.py,sha256=o433oA_nUCtOPaMEGc8VEhZIKa71imVHXFw7TsXaP8M,1263
+greenlet/tests/fail_cpp_exception.py,sha256=o_ZbipWikok8Bjc-vjiQvcb5FHh2nVW-McGKMLcMzh0,985
+greenlet/tests/fail_initialstub_already_started.py,sha256=txENn5IyzGx2p-XR1XB7qXmC8JX_4mKDEA8kYBXUQKc,1961
+greenlet/tests/fail_slp_switch.py,sha256=rJBZcZfTWR3e2ERQtPAud6YKShiDsP84PmwOJbp4ey0,524
+greenlet/tests/fail_switch_three_greenlets.py,sha256=zSitV7rkNnaoHYVzAGGLnxz-yPtohXJJzaE8ehFDQ0M,956
+greenlet/tests/fail_switch_three_greenlets2.py,sha256=FPJensn2EJxoropl03JSTVP3kgP33k04h6aDWWozrOk,1285
+greenlet/tests/fail_switch_two_greenlets.py,sha256=1CaI8s3504VbbF1vj1uBYuy-zxBHVzHPIAd1LIc8ONg,817
+greenlet/tests/leakcheck.py,sha256=SgPOQ5_vttOiLDsCOV6wXvvXRxy6noNHqEwctTC5Vpc,11929
+greenlet/tests/test_contextvars.py,sha256=kPHW_QxQopTBxBlDix4Vs9erPp4_Elx5_N02e7_9VCQ,10305
+greenlet/tests/test_cpp.py,sha256=42pJVjNmAYiNR35_TN8YyLrnFW8bzuiOL4XnK1PTmWQ,2448
+greenlet/tests/test_extension_interface.py,sha256=eJ3cwLacdK2WbsrC-4DgeyHdwLRcG4zx7rrkRtqSzC4,3829
+greenlet/tests/test_gc.py,sha256=nf4pgF0eUz8tUYQGPHRPWQZPslztN-FfxvD4EONIpmw,2916
+greenlet/tests/test_generator.py,sha256=tONXiTf98VGm347o1b-810daPiwdla5cbpFg6QI1R1g,1240
+greenlet/tests/test_generator_nested.py,sha256=gMTDwBb5Rx4UcuYYp31YufLONLXruVDaCcKlJ4UIk64,3720
+greenlet/tests/test_greenlet.py,sha256=smMZkBmJVgBHo1Wcwcdt71anFK-XoU5-r_W4oae4e7M,43091
+greenlet/tests/test_greenlet_trash.py,sha256=P6r-3K4fmXX8foW8BVgthuqVKjicHMDvxfK7Al4x028,7508
+greenlet/tests/test_leaks.py,sha256=wskLqCAvqZ3qTZkam_wXzd-E5zelUjlXS5Ss8KshtZY,17465
+greenlet/tests/test_stack_saved.py,sha256=eyzqNY2VCGuGlxhT_In6TvZ6Okb0AXFZVyBEnK1jDwA,446
+greenlet/tests/test_throw.py,sha256=cowzx8900jpKon8-N4-UwsGH9ox5hfsqtDoVUNat84g,3734
+greenlet/tests/test_tracing.py,sha256=VlwzMU0C1noospZhuUMyB7MHw200emIvGCN_6G2p2ZU,8250
+greenlet/tests/test_version.py,sha256=O9DpAITsOFgiRcjd4odQ7ejmwx_N9Q1zQENVcbtFHIc,1339
+greenlet/tests/test_weakref.py,sha256=NWOaaJOMn83oKdXGoGzGAswb-QRHprlF2f0-4igjZMI,898
diff --git a/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/WHEEL b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/WHEEL
new file mode 100644
index 000000000..f25312245
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.41.2)
+Root-Is-Purelib: false
+Tag: cp310-cp310-manylinux_2_24_x86_64
+Tag: cp310-cp310-manylinux_2_28_x86_64
+
diff --git a/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/top_level.txt
new file mode 100644
index 000000000..46725be4f
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet-3.0.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+greenlet
diff --git a/venv/lib/python3.10/site-packages/greenlet/TBrokenGreenlet.cpp b/venv/lib/python3.10/site-packages/greenlet/TBrokenGreenlet.cpp
new file mode 100644
index 000000000..11a3beab8
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/TBrokenGreenlet.cpp
@@ -0,0 +1,45 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+/**
+ * Implementation of greenlet::UserGreenlet.
+ *
+ * Format with:
+ * clang-format -i --style=file src/greenlet/greenlet.c
+ *
+ *
+ * Fix missing braces with:
+ * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
+*/
+
+#include "greenlet_greenlet.hpp"
+
+namespace greenlet {
+
+void* BrokenGreenlet::operator new(size_t UNUSED(count))
+{
+ return allocator.allocate(1);
+}
+
+
+void BrokenGreenlet::operator delete(void* ptr)
+{
+ return allocator.deallocate(static_cast(ptr),
+ 1);
+}
+
+greenlet::PythonAllocator greenlet::BrokenGreenlet::allocator;
+
+bool
+BrokenGreenlet::force_slp_switch_error() const noexcept
+{
+ return this->_force_slp_switch_error;
+}
+
+UserGreenlet::switchstack_result_t BrokenGreenlet::g_switchstack(void)
+{
+ if (this->_force_switch_error) {
+ return switchstack_result_t(-1);
+ }
+ return UserGreenlet::g_switchstack();
+}
+
+}; //namespace greenlet
diff --git a/venv/lib/python3.10/site-packages/greenlet/TExceptionState.cpp b/venv/lib/python3.10/site-packages/greenlet/TExceptionState.cpp
new file mode 100644
index 000000000..ee6b19177
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/TExceptionState.cpp
@@ -0,0 +1,62 @@
+#ifndef GREENLET_EXCEPTION_STATE_CPP
+#define GREENLET_EXCEPTION_STATE_CPP
+
+#include
+#include "greenlet_greenlet.hpp"
+
+namespace greenlet {
+
+
+ExceptionState::ExceptionState()
+{
+ this->clear();
+}
+
+void ExceptionState::operator<<(const PyThreadState *const tstate) noexcept
+{
+ this->exc_info = tstate->exc_info;
+ this->exc_state = tstate->exc_state;
+}
+
+void ExceptionState::operator>>(PyThreadState *const tstate) noexcept
+{
+ tstate->exc_state = this->exc_state;
+ tstate->exc_info =
+ this->exc_info ? this->exc_info : &tstate->exc_state;
+ this->clear();
+}
+
+void ExceptionState::clear() noexcept
+{
+ this->exc_info = nullptr;
+ this->exc_state.exc_value = nullptr;
+#if !GREENLET_PY311
+ this->exc_state.exc_type = nullptr;
+ this->exc_state.exc_traceback = nullptr;
+#endif
+ this->exc_state.previous_item = nullptr;
+}
+
+int ExceptionState::tp_traverse(visitproc visit, void* arg) noexcept
+{
+ Py_VISIT(this->exc_state.exc_value);
+#if !GREENLET_PY311
+ Py_VISIT(this->exc_state.exc_type);
+ Py_VISIT(this->exc_state.exc_traceback);
+#endif
+ return 0;
+}
+
+void ExceptionState::tp_clear() noexcept
+{
+ Py_CLEAR(this->exc_state.exc_value);
+#if !GREENLET_PY311
+ Py_CLEAR(this->exc_state.exc_type);
+ Py_CLEAR(this->exc_state.exc_traceback);
+#endif
+}
+
+
+}; // namespace greenlet
+
+#endif // GREENLET_EXCEPTION_STATE_CPP
diff --git a/venv/lib/python3.10/site-packages/greenlet/TGreenlet.cpp b/venv/lib/python3.10/site-packages/greenlet/TGreenlet.cpp
new file mode 100644
index 000000000..60acab335
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/TGreenlet.cpp
@@ -0,0 +1,610 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+/**
+ * Implementation of greenlet::Greenlet.
+ *
+ * Format with:
+ * clang-format -i --style=file src/greenlet/greenlet.c
+ *
+ *
+ * Fix missing braces with:
+ * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
+*/
+
+#include "greenlet_internal.hpp"
+#include "greenlet_greenlet.hpp"
+#include "greenlet_thread_state.hpp"
+
+#include "TGreenletGlobals.cpp"
+#include "TThreadStateDestroy.cpp"
+
+namespace greenlet {
+
+Greenlet::Greenlet(PyGreenlet* p)
+{
+ p ->pimpl = this;
+}
+
+Greenlet::~Greenlet()
+{
+ // XXX: Can't do this. tp_clear is a virtual function, and by the
+ // time we're here, we've sliced off our child classes.
+ //this->tp_clear();
+}
+
+Greenlet::Greenlet(PyGreenlet* p, const StackState& initial_stack)
+ : stack_state(initial_stack)
+{
+ // can't use a delegating constructor because of
+ // MSVC for Python 2.7
+ p->pimpl = this;
+}
+
+bool
+Greenlet::force_slp_switch_error() const noexcept
+{
+ return false;
+}
+
+void
+Greenlet::release_args()
+{
+ this->switch_args.CLEAR();
+}
+
+/**
+ * CAUTION: This will allocate memory and may trigger garbage
+ * collection and arbitrary Python code.
+ */
+OwnedObject
+Greenlet::throw_GreenletExit_during_dealloc(const ThreadState& UNUSED(current_thread_state))
+{
+ // If we're killed because we lost all references in the
+ // middle of a switch, that's ok. Don't reset the args/kwargs,
+ // we still want to pass them to the parent.
+ PyErr_SetString(mod_globs->PyExc_GreenletExit,
+ "Killing the greenlet because all references have vanished.");
+ // To get here it had to have run before
+ return this->g_switch();
+}
+
+inline void
+Greenlet::slp_restore_state() noexcept
+{
+#ifdef SLP_BEFORE_RESTORE_STATE
+ SLP_BEFORE_RESTORE_STATE();
+#endif
+ this->stack_state.copy_heap_to_stack(
+ this->thread_state()->borrow_current()->stack_state);
+}
+
+
+inline int
+Greenlet::slp_save_state(char *const stackref) noexcept
+{
+ // XXX: This used to happen in the middle, before saving, but
+ // after finding the next owner. Does that matter? This is
+ // only defined for Sparc/GCC where it flushes register
+ // windows to the stack (I think)
+#ifdef SLP_BEFORE_SAVE_STATE
+ SLP_BEFORE_SAVE_STATE();
+#endif
+ return this->stack_state.copy_stack_to_heap(stackref,
+ this->thread_state()->borrow_current()->stack_state);
+}
+
+/**
+ * CAUTION: This will allocate memory and may trigger garbage
+ * collection and arbitrary Python code.
+ */
+OwnedObject
+Greenlet::on_switchstack_or_initialstub_failure(
+ Greenlet* target,
+ const Greenlet::switchstack_result_t& err,
+ const bool target_was_me,
+ const bool was_initial_stub)
+{
+ // If we get here, either g_initialstub()
+ // failed, or g_switchstack() failed. Either one of those
+ // cases SHOULD leave us in the original greenlet with a valid stack.
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(
+ PyExc_SystemError,
+ was_initial_stub
+ ? "Failed to switch stacks into a greenlet for the first time."
+ : "Failed to switch stacks into a running greenlet.");
+ }
+ this->release_args();
+
+ if (target && !target_was_me) {
+ target->murder_in_place();
+ }
+
+ assert(!err.the_new_current_greenlet);
+ assert(!err.origin_greenlet);
+ return OwnedObject();
+
+}
+
+OwnedGreenlet
+Greenlet::g_switchstack_success() noexcept
+{
+ PyThreadState* tstate = PyThreadState_GET();
+ // restore the saved state
+ this->python_state >> tstate;
+ this->exception_state >> tstate;
+
+ // The thread state hasn't been changed yet.
+ ThreadState* thread_state = this->thread_state();
+ OwnedGreenlet result(thread_state->get_current());
+ thread_state->set_current(this->self());
+ //assert(thread_state->borrow_current().borrow() == this->_self);
+ return result;
+}
+
+Greenlet::switchstack_result_t
+Greenlet::g_switchstack(void)
+{
+ // if any of these assertions fail, it's likely because we
+ // switched away and tried to switch back to us. Early stages of
+ // switching are not reentrant because we re-use ``this->args()``.
+ // Switching away would happen if we trigger a garbage collection
+ // (by just using some Python APIs that happen to allocate Python
+ // objects) and some garbage had weakref callbacks or __del__ that
+ // switches (people don't write code like that by hand, but with
+ // gevent it's possible without realizing it)
+ assert(this->args() || PyErr_Occurred());
+ { /* save state */
+ if (this->thread_state()->is_current(this->self())) {
+ // Hmm, nothing to do.
+ // TODO: Does this bypass trace events that are
+ // important?
+ return switchstack_result_t(0,
+ this, this->thread_state()->borrow_current());
+ }
+ BorrowedGreenlet current = this->thread_state()->borrow_current();
+ PyThreadState* tstate = PyThreadState_GET();
+
+ current->python_state << tstate;
+ current->exception_state << tstate;
+ this->python_state.will_switch_from(tstate);
+ switching_thread_state = this;
+ }
+ assert(this->args() || PyErr_Occurred());
+ // If this is the first switch into a greenlet, this will
+ // return twice, once with 1 in the new greenlet, once with 0
+ // in the origin.
+ int err;
+ if (this->force_slp_switch_error()) {
+ err = -1;
+ }
+ else {
+ err = slp_switch();
+ }
+
+ if (err < 0) { /* error */
+ // Tested by
+ // test_greenlet.TestBrokenGreenlets.test_failed_to_slp_switch_into_running
+ //
+ // It's not clear if it's worth trying to clean up and
+ // continue here. Failing to switch stacks is a big deal which
+ // may not be recoverable (who knows what state the stack is in).
+ // Also, we've stolen references in preparation for calling
+ // ``g_switchstack_success()`` and we don't have a clean
+ // mechanism for backing that all out.
+ Py_FatalError("greenlet: Failed low-level slp_switch(). The stack is probably corrupt.");
+ }
+
+ // No stack-based variables are valid anymore.
+
+ // But the global is volatile so we can reload it without the
+ // compiler caching it from earlier.
+ Greenlet* greenlet_that_switched_in = switching_thread_state; // aka this
+ switching_thread_state = nullptr;
+ // except that no stack variables are valid, we would:
+ // assert(this == greenlet_that_switched_in);
+
+ // switchstack success is where we restore the exception state,
+ // etc. It returns the origin greenlet because its convenient.
+
+ OwnedGreenlet origin = greenlet_that_switched_in->g_switchstack_success();
+ assert(greenlet_that_switched_in->args() || PyErr_Occurred());
+ return switchstack_result_t(err, greenlet_that_switched_in, origin);
+}
+
+
+inline void
+Greenlet::check_switch_allowed() const
+{
+ // TODO: Make this take a parameter of the current greenlet,
+ // or current main greenlet, to make the check for
+ // cross-thread switching cheaper. Surely somewhere up the
+ // call stack we've already accessed the thread local variable.
+
+ // We expect to always have a main greenlet now; accessing the thread state
+ // created it. However, if we get here and cleanup has already
+ // begun because we're a greenlet that was running in a
+ // (now dead) thread, these invariants will not hold true. In
+ // fact, accessing `this->thread_state` may not even be possible.
+
+ // If the thread this greenlet was running in is dead,
+ // we'll still have a reference to a main greenlet, but the
+ // thread state pointer we have is bogus.
+ // TODO: Give the objects an API to determine if they belong
+ // to a dead thread.
+
+ const BorrowedMainGreenlet main_greenlet = this->find_main_greenlet_in_lineage();
+
+ if (!main_greenlet) {
+ throw PyErrOccurred(mod_globs->PyExc_GreenletError,
+ "cannot switch to a garbage collected greenlet");
+ }
+
+ if (!main_greenlet->thread_state()) {
+ throw PyErrOccurred(mod_globs->PyExc_GreenletError,
+ "cannot switch to a different thread (which happens to have exited)");
+ }
+
+ // The main greenlet we found was from the .parent lineage.
+ // That may or may not have any relationship to the main
+ // greenlet of the running thread. We can't actually access
+ // our this->thread_state members to try to check that,
+ // because it could be in the process of getting destroyed,
+ // but setting the main_greenlet->thread_state member to NULL
+ // may not be visible yet. So we need to check against the
+ // current thread state (once the cheaper checks are out of
+ // the way)
+ const BorrowedMainGreenlet current_main_greenlet = GET_THREAD_STATE().state().borrow_main_greenlet();
+ if (
+ // lineage main greenlet is not this thread's greenlet
+ current_main_greenlet != main_greenlet
+ || (
+ // atteched to some thread
+ this->main_greenlet()
+ // XXX: Same condition as above. Was this supposed to be
+ // this->main_greenlet()?
+ && current_main_greenlet != main_greenlet)
+ // switching into a known dead thread (XXX: which, if we get here,
+ // is bad, because we just accessed the thread state, which is
+ // gone!)
+ || (!current_main_greenlet->thread_state())) {
+ // CAUTION: This may trigger memory allocations, gc, and
+ // arbitrary Python code.
+ throw PyErrOccurred(mod_globs->PyExc_GreenletError,
+ "cannot switch to a different thread");
+ }
+}
+
+const OwnedObject
+Greenlet::context() const
+{
+ using greenlet::PythonStateContext;
+ OwnedObject result;
+
+ if (this->is_currently_running_in_some_thread()) {
+ /* Currently running greenlet: context is stored in the thread state,
+ not the greenlet object. */
+ if (GET_THREAD_STATE().state().is_current(this->self())) {
+ result = PythonStateContext::context(PyThreadState_GET());
+ }
+ else {
+ throw ValueError(
+ "cannot get context of a "
+ "greenlet that is running in a different thread");
+ }
+ }
+ else {
+ /* Greenlet is not running: just return context. */
+ result = this->python_state.context();
+ }
+ if (!result) {
+ result = OwnedObject::None();
+ }
+ return result;
+}
+
+
+void
+Greenlet::context(BorrowedObject given)
+{
+ using greenlet::PythonStateContext;
+ if (!given) {
+ throw AttributeError("can't delete context attribute");
+ }
+ if (given.is_None()) {
+ /* "Empty context" is stored as NULL, not None. */
+ given = nullptr;
+ }
+
+ //checks type, incrs refcnt
+ greenlet::refs::OwnedContext context(given);
+ PyThreadState* tstate = PyThreadState_GET();
+
+ if (this->is_currently_running_in_some_thread()) {
+ if (!GET_THREAD_STATE().state().is_current(this->self())) {
+ throw ValueError("cannot set context of a greenlet"
+ " that is running in a different thread");
+ }
+
+ /* Currently running greenlet: context is stored in the thread state,
+ not the greenlet object. */
+ OwnedObject octx = OwnedObject::consuming(PythonStateContext::context(tstate));
+ PythonStateContext::context(tstate, context.relinquish_ownership());
+ }
+ else {
+ /* Greenlet is not running: just set context. Note that the
+ greenlet may be dead.*/
+ this->python_state.context() = context;
+ }
+}
+
+/**
+ * CAUTION: May invoke arbitrary Python code.
+ *
+ * Figure out what the result of ``greenlet.switch(arg, kwargs)``
+ * should be and transfers ownership of it to the left-hand-side.
+ *
+ * If switch() was just passed an arg tuple, then we'll just return that.
+ * If only keyword arguments were passed, then we'll pass the keyword
+ * argument dict. Otherwise, we'll create a tuple of (args, kwargs) and
+ * return both.
+ *
+ * CAUTION: This may allocate a new tuple object, which may
+ * cause the Python garbage collector to run, which in turn may
+ * run arbitrary Python code that switches.
+ */
+OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept
+{
+ // Because this may invoke arbitrary Python code, which could
+ // result in switching back to us, we need to get the
+ // arguments locally on the stack.
+ assert(rhs);
+ OwnedObject args = rhs.args();
+ OwnedObject kwargs = rhs.kwargs();
+ rhs.CLEAR();
+ // We shouldn't be called twice for the same switch.
+ assert(args || kwargs);
+ assert(!rhs);
+
+ if (!kwargs) {
+ lhs = args;
+ }
+ else if (!PyDict_Size(kwargs.borrow())) {
+ lhs = args;
+ }
+ else if (!PySequence_Length(args.borrow())) {
+ lhs = kwargs;
+ }
+ else {
+ // PyTuple_Pack allocates memory, may GC, may run arbitrary
+ // Python code.
+ lhs = OwnedObject::consuming(PyTuple_Pack(2, args.borrow(), kwargs.borrow()));
+ }
+ return lhs;
+}
+
+static OwnedObject
+g_handle_exit(const OwnedObject& greenlet_result)
+{
+ if (!greenlet_result && mod_globs->PyExc_GreenletExit.PyExceptionMatches()) {
+ /* catch and ignore GreenletExit */
+ PyErrFetchParam val;
+ PyErr_Fetch(PyErrFetchParam(), val, PyErrFetchParam());
+ if (!val) {
+ return OwnedObject::None();
+ }
+ return OwnedObject(val);
+ }
+
+ if (greenlet_result) {
+ // package the result into a 1-tuple
+ // PyTuple_Pack increments the reference of its arguments,
+ // so we always need to decref the greenlet result;
+ // the owner will do that.
+ return OwnedObject::consuming(PyTuple_Pack(1, greenlet_result.borrow()));
+ }
+
+ return OwnedObject();
+}
+
+
+
+/**
+ * May run arbitrary Python code.
+ */
+OwnedObject
+Greenlet::g_switch_finish(const switchstack_result_t& err)
+{
+ assert(err.the_new_current_greenlet == this);
+
+ ThreadState& state = *this->thread_state();
+ // Because calling the trace function could do arbitrary things,
+ // including switching away from this greenlet and then maybe
+ // switching back, we need to capture the arguments now so that
+ // they don't change.
+ OwnedObject result;
+ if (this->args()) {
+ result <<= this->args();
+ }
+ else {
+ assert(PyErr_Occurred());
+ }
+ assert(!this->args());
+ try {
+ // Our only caller handles the bad error case
+ assert(err.status >= 0);
+ assert(state.borrow_current() == this->self());
+ if (OwnedObject tracefunc = state.get_tracefunc()) {
+ assert(result || PyErr_Occurred());
+ g_calltrace(tracefunc,
+ result ? mod_globs->event_switch : mod_globs->event_throw,
+ err.origin_greenlet,
+ this->self());
+ }
+ // The above could have invoked arbitrary Python code, but
+ // it couldn't switch back to this object and *also*
+ // throw an exception, so the args won't have changed.
+
+ if (PyErr_Occurred()) {
+ // We get here if we fell of the end of the run() function
+ // raising an exception. The switch itself was
+ // successful, but the function raised.
+ // valgrind reports that memory allocated here can still
+ // be reached after a test run.
+ throw PyErrOccurred::from_current();
+ }
+ return result;
+ }
+ catch (const PyErrOccurred&) {
+ /* Turn switch errors into switch throws */
+ /* Turn trace errors into switch throws */
+ this->release_args();
+ throw;
+ }
+}
+
+void
+Greenlet::g_calltrace(const OwnedObject& tracefunc,
+ const greenlet::refs::ImmortalEventName& event,
+ const BorrowedGreenlet& origin,
+ const BorrowedGreenlet& target)
+{
+ PyErrPieces saved_exc;
+ try {
+ TracingGuard tracing_guard;
+ // TODO: We have saved the active exception (if any) that's
+ // about to be raised. In the 'throw' case, we could provide
+ // the exception to the tracefunction, which seems very helpful.
+ tracing_guard.CallTraceFunction(tracefunc, event, origin, target);
+ }
+ catch (const PyErrOccurred&) {
+ // In case of exceptions trace function is removed,
+ // and any existing exception is replaced with the tracing
+ // exception.
+ GET_THREAD_STATE().state().set_tracefunc(Py_None);
+ throw;
+ }
+
+ saved_exc.PyErrRestore();
+ assert(
+ (event == mod_globs->event_throw && PyErr_Occurred())
+ || (event == mod_globs->event_switch && !PyErr_Occurred())
+ );
+}
+
+void
+Greenlet::murder_in_place()
+{
+ if (this->active()) {
+ assert(!this->is_currently_running_in_some_thread());
+ this->deactivate_and_free();
+ }
+}
+
+inline void
+Greenlet::deactivate_and_free()
+{
+ if (!this->active()) {
+ return;
+ }
+ // Throw away any saved stack.
+ this->stack_state = StackState();
+ assert(!this->stack_state.active());
+ // Throw away any Python references.
+ // We're holding a borrowed reference to the last
+ // frame we executed. Since we borrowed it, the
+ // normal traversal, clear, and dealloc functions
+ // ignore it, meaning it leaks. (The thread state
+ // object can't find it to clear it when that's
+ // deallocated either, because by definition if we
+ // got an object on this list, it wasn't
+ // running and the thread state doesn't have
+ // this frame.)
+ // So here, we *do* clear it.
+ this->python_state.tp_clear(true);
+}
+
+bool
+Greenlet::belongs_to_thread(const ThreadState* thread_state) const
+{
+ if (!this->thread_state() // not running anywhere, or thread
+ // exited
+ || !thread_state) { // same, or there is no thread state.
+ return false;
+ }
+ return true;
+}
+
+
+void
+Greenlet::deallocing_greenlet_in_thread(const ThreadState* current_thread_state)
+{
+ /* Cannot raise an exception to kill the greenlet if
+ it is not running in the same thread! */
+ if (this->belongs_to_thread(current_thread_state)) {
+ assert(current_thread_state);
+ // To get here it had to have run before
+ /* Send the greenlet a GreenletExit exception. */
+
+ // We don't care about the return value, only whether an
+ // exception happened.
+ this->throw_GreenletExit_during_dealloc(*current_thread_state);
+ return;
+ }
+
+ // Not the same thread! Temporarily save the greenlet
+ // into its thread's deleteme list, *if* it exists.
+ // If that thread has already exited, and processed its pending
+ // cleanup, we'll never be able to clean everything up: we won't
+ // be able to raise an exception.
+ // That's mostly OK! Since we can't add it to a list, our refcount
+ // won't increase, and we'll go ahead with the DECREFs later.
+ ThreadState *const thread_state = this->thread_state();
+ if (thread_state) {
+ thread_state->delete_when_thread_running(this->self());
+ }
+ else {
+ // The thread is dead, we can't raise an exception.
+ // We need to make it look non-active, though, so that dealloc
+ // finishes killing it.
+ this->deactivate_and_free();
+ }
+ return;
+}
+
+
+int
+Greenlet::tp_traverse(visitproc visit, void* arg)
+{
+
+ int result;
+ if ((result = this->exception_state.tp_traverse(visit, arg)) != 0) {
+ return result;
+ }
+ //XXX: This is ugly. But so is handling everything having to do
+ //with the top frame.
+ bool visit_top_frame = this->was_running_in_dead_thread();
+ // When true, the thread is dead. Our implicit weak reference to the
+ // frame is now all that's left; we consider ourselves to
+ // strongly own it now.
+ if ((result = this->python_state.tp_traverse(visit, arg, visit_top_frame)) != 0) {
+ return result;
+ }
+ return 0;
+}
+
+int
+Greenlet::tp_clear()
+{
+ bool own_top_frame = this->was_running_in_dead_thread();
+ this->exception_state.tp_clear();
+ this->python_state.tp_clear(own_top_frame);
+ return 0;
+}
+
+bool Greenlet::is_currently_running_in_some_thread() const
+{
+ return this->stack_state.active() && !this->python_state.top_frame();
+}
+
+
+}; // namespace greenlet
diff --git a/venv/lib/python3.10/site-packages/greenlet/TGreenletGlobals.cpp b/venv/lib/python3.10/site-packages/greenlet/TGreenletGlobals.cpp
new file mode 100644
index 000000000..c71c963b3
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/TGreenletGlobals.cpp
@@ -0,0 +1,94 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+/**
+ * Implementation of GreenletGlobals.
+ *
+ * Format with:
+ * clang-format -i --style=file src/greenlet/greenlet.c
+ *
+ *
+ * Fix missing braces with:
+ * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
+*/
+#ifndef T_GREENLET_GLOBALS
+#define T_GREENLET_GLOBALS
+
+#include "greenlet_refs.hpp"
+#include "greenlet_exceptions.hpp"
+#include "greenlet_thread_support.hpp"
+#include "greenlet_thread_state.hpp"
+
+namespace greenlet {
+
+// This encapsulates what were previously module global "constants"
+// established at init time.
+// This is a step towards Python3 style module state that allows
+// reloading.
+//
+// In an earlier iteration of this code, we used placement new to be
+// able to allocate this object statically still, so that references
+// to its members don't incur an extra pointer indirection.
+// But under some scenarios, that could result in crashes at
+// shutdown because apparently the destructor was getting run twice?
+class GreenletGlobals
+{
+
+public:
+ const greenlet::refs::ImmortalEventName event_switch;
+ const greenlet::refs::ImmortalEventName event_throw;
+ const greenlet::refs::ImmortalException PyExc_GreenletError;
+ const greenlet::refs::ImmortalException PyExc_GreenletExit;
+ const greenlet::refs::ImmortalObject empty_tuple;
+ const greenlet::refs::ImmortalObject empty_dict;
+ const greenlet::refs::ImmortalString str_run;
+ Mutex* const thread_states_to_destroy_lock;
+ greenlet::cleanup_queue_t thread_states_to_destroy;
+
+ GreenletGlobals() :
+ event_switch("switch"),
+ event_throw("throw"),
+ PyExc_GreenletError("greenlet.error"),
+ PyExc_GreenletExit("greenlet.GreenletExit", PyExc_BaseException),
+ empty_tuple(Require(PyTuple_New(0))),
+ empty_dict(Require(PyDict_New())),
+ str_run("run"),
+ thread_states_to_destroy_lock(new Mutex())
+ {}
+
+ ~GreenletGlobals()
+ {
+ // This object is (currently) effectively immortal, and not
+ // just because of those placement new tricks; if we try to
+ // deallocate the static object we allocated, and overwrote,
+ // we would be doing so at C++ teardown time, which is after
+ // the final Python GIL is released, and we can't use the API
+ // then.
+ // (The members will still be destructed, but they also don't
+ // do any deallocation.)
+ }
+
+ void queue_to_destroy(ThreadState* ts) const
+ {
+ // we're currently accessed through a static const object,
+ // implicitly marking our members as const, so code can't just
+ // call push_back (or pop_back) without casting away the
+ // const.
+ //
+ // Do that for callers.
+ greenlet::cleanup_queue_t& q = const_cast(this->thread_states_to_destroy);
+ q.push_back(ts);
+ }
+
+ ThreadState* take_next_to_destroy() const
+ {
+ greenlet::cleanup_queue_t& q = const_cast(this->thread_states_to_destroy);
+ ThreadState* result = q.back();
+ q.pop_back();
+ return result;
+ }
+};
+
+}; // namespace greenlet
+
+static const greenlet::GreenletGlobals* mod_globs;
+
+#endif // T_GREENLET_GLOBALS
diff --git a/venv/lib/python3.10/site-packages/greenlet/TMainGreenlet.cpp b/venv/lib/python3.10/site-packages/greenlet/TMainGreenlet.cpp
new file mode 100644
index 000000000..c33aadb3a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/TMainGreenlet.cpp
@@ -0,0 +1,155 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+/**
+ * Implementation of greenlet::MainGreenlet.
+ *
+ * Format with:
+ * clang-format -i --style=file src/greenlet/greenlet.c
+ *
+ *
+ * Fix missing braces with:
+ * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
+*/
+
+#include "greenlet_greenlet.hpp"
+#include "greenlet_thread_state.hpp"
+
+
+// Protected by the GIL. Incremented when we create a main greenlet,
+// in a new thread, decremented when it is destroyed.
+static Py_ssize_t G_TOTAL_MAIN_GREENLETS;
+
+namespace greenlet {
+greenlet::PythonAllocator MainGreenlet::allocator;
+
+void* MainGreenlet::operator new(size_t UNUSED(count))
+{
+ return allocator.allocate(1);
+}
+
+
+void MainGreenlet::operator delete(void* ptr)
+{
+ return allocator.deallocate(static_cast(ptr),
+ 1);
+}
+
+
+MainGreenlet::MainGreenlet(PyGreenlet* p, ThreadState* state)
+ : Greenlet(p, StackState::make_main()),
+ _self(p),
+ _thread_state(state)
+{
+ G_TOTAL_MAIN_GREENLETS++;
+}
+
+MainGreenlet::~MainGreenlet()
+{
+ G_TOTAL_MAIN_GREENLETS--;
+ this->tp_clear();
+}
+
+ThreadState*
+MainGreenlet::thread_state() const noexcept
+{
+ return this->_thread_state;
+}
+
+void
+MainGreenlet::thread_state(ThreadState* t) noexcept
+{
+ assert(!t);
+ this->_thread_state = t;
+}
+
+BorrowedGreenlet
+MainGreenlet::self() const noexcept
+{
+ return BorrowedGreenlet(this->_self.borrow());
+}
+
+
+const BorrowedMainGreenlet
+MainGreenlet::main_greenlet() const
+{
+ return this->_self;
+}
+
+BorrowedMainGreenlet
+MainGreenlet::find_main_greenlet_in_lineage() const
+{
+ return BorrowedMainGreenlet(this->_self);
+}
+
+bool
+MainGreenlet::was_running_in_dead_thread() const noexcept
+{
+ return !this->_thread_state;
+}
+
+OwnedObject
+MainGreenlet::g_switch()
+{
+ try {
+ this->check_switch_allowed();
+ }
+ catch (const PyErrOccurred&) {
+ this->release_args();
+ throw;
+ }
+
+ switchstack_result_t err = this->g_switchstack();
+ if (err.status < 0) {
+ // XXX: This code path is untested, but it is shared
+ // with the UserGreenlet path that is tested.
+ return this->on_switchstack_or_initialstub_failure(
+ this,
+ err,
+ true, // target was me
+ false // was initial stub
+ );
+ }
+
+ return err.the_new_current_greenlet->g_switch_finish(err);
+}
+
+int
+MainGreenlet::tp_traverse(visitproc visit, void* arg)
+{
+ if (this->_thread_state) {
+ // we've already traversed main, (self), don't do it again.
+ int result = this->_thread_state->tp_traverse(visit, arg, false);
+ if (result) {
+ return result;
+ }
+ }
+ return Greenlet::tp_traverse(visit, arg);
+}
+
+const OwnedObject&
+MainGreenlet::run() const
+{
+ throw AttributeError("Main greenlets do not have a run attribute.");
+}
+
+void
+MainGreenlet::run(const BorrowedObject UNUSED(nrun))
+{
+ throw AttributeError("Main greenlets do not have a run attribute.");
+}
+
+void
+MainGreenlet::parent(const BorrowedObject raw_new_parent)
+{
+ if (!raw_new_parent) {
+ throw AttributeError("can't delete attribute");
+ }
+ throw AttributeError("cannot set the parent of a main greenlet");
+}
+
+const OwnedGreenlet
+MainGreenlet::parent() const
+{
+ return OwnedGreenlet(); // null becomes None
+}
+
+}; // namespace greenlet
diff --git a/venv/lib/python3.10/site-packages/greenlet/TPythonState.cpp b/venv/lib/python3.10/site-packages/greenlet/TPythonState.cpp
new file mode 100644
index 000000000..9d57155c4
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/TPythonState.cpp
@@ -0,0 +1,368 @@
+#ifndef GREENLET_PYTHON_STATE_CPP
+#define GREENLET_PYTHON_STATE_CPP
+
+#include
+#include "greenlet_greenlet.hpp"
+
+namespace greenlet {
+
+PythonState::PythonState()
+ : _top_frame()
+#if GREENLET_USE_CFRAME
+ ,cframe(nullptr)
+ ,use_tracing(0)
+#endif
+#if GREENLET_PY312
+ ,py_recursion_depth(0)
+ ,c_recursion_depth(0)
+#else
+ ,recursion_depth(0)
+#endif
+ ,trash_delete_nesting(0)
+#if GREENLET_PY311
+ ,current_frame(nullptr)
+ ,datastack_chunk(nullptr)
+ ,datastack_top(nullptr)
+ ,datastack_limit(nullptr)
+#endif
+#if GREENLET_PY312
+ ,_prev_frame(nullptr)
+#endif
+{
+#if GREENLET_USE_CFRAME
+ /*
+ The PyThreadState->cframe pointer usually points to memory on
+ the stack, alloceted in a call into PyEval_EvalFrameDefault.
+
+ Initially, before any evaluation begins, it points to the
+ initial PyThreadState object's ``root_cframe`` object, which is
+ statically allocated for the lifetime of the thread.
+
+ A greenlet can last for longer than a call to
+ PyEval_EvalFrameDefault, so we can't set its ``cframe`` pointer
+ to be the current ``PyThreadState->cframe``; nor could we use
+ one from the greenlet parent for the same reason. Yet a further
+ no: we can't allocate one scoped to the greenlet and then
+ destroy it when the greenlet is deallocated, because inside the
+ interpreter the _PyCFrame objects form a linked list, and that too
+ can result in accessing memory beyond its dynamic lifetime (if
+ the greenlet doesn't actually finish before it dies, its entry
+ could still be in the list).
+
+ Using the ``root_cframe`` is problematic, though, because its
+ members are never modified by the interpreter and are set to 0,
+ meaning that its ``use_tracing`` flag is never updated. We don't
+ want to modify that value in the ``root_cframe`` ourself: it
+ *shouldn't* matter much because we should probably never get
+ back to the point where that's the only cframe on the stack;
+ even if it did matter, the major consequence of an incorrect
+ value for ``use_tracing`` is that if its true the interpreter
+ does some extra work --- however, it's just good code hygiene.
+
+ Our solution: before a greenlet runs, after its initial
+ creation, it uses the ``root_cframe`` just to have something to
+ put there. However, once the greenlet is actually switched to
+ for the first time, ``g_initialstub`` (which doesn't actually
+ "return" while the greenlet is running) stores a new _PyCFrame on
+ its local stack, and copies the appropriate values from the
+ currently running _PyCFrame; this is then made the _PyCFrame for the
+ newly-minted greenlet. ``g_initialstub`` then proceeds to call
+ ``glet.run()``, which results in ``PyEval_...`` adding the
+ _PyCFrame to the list. Switches continue as normal. Finally, when
+ the greenlet finishes, the call to ``glet.run()`` returns and
+ the _PyCFrame is taken out of the linked list and the stack value
+ is now unused and free to expire.
+
+ XXX: I think we can do better. If we're deallocing in the same
+ thread, can't we traverse the list and unlink our frame?
+ Can we just keep a reference to the thread state in case we
+ dealloc in another thread? (Is that even possible if we're still
+ running and haven't returned from g_initialstub?)
+ */
+ this->cframe = &PyThreadState_GET()->root_cframe;
+#endif
+}
+
+
+inline void PythonState::may_switch_away() noexcept
+{
+#if GREENLET_PY311
+ // PyThreadState_GetFrame is probably going to have to allocate a
+ // new frame object. That may trigger garbage collection. Because
+ // we call this during the early phases of a switch (it doesn't
+ // matter to which greenlet, as this has a global effect), if a GC
+ // triggers a switch away, two things can happen, both bad:
+ // - We might not get switched back to, halting forward progress.
+ // this is pathological, but possible.
+ // - We might get switched back to with a different set of
+ // arguments or a throw instead of a switch. That would corrupt
+ // our state (specifically, PyErr_Occurred() and this->args()
+ // would no longer agree).
+ //
+ // Thus, when we call this API, we need to have GC disabled.
+ // This method serves as a bottleneck we call when maybe beginning
+ // a switch. In this way, it is always safe -- no risk of GC -- to
+ // use ``_GetFrame()`` whenever we need to, just as it was in
+ // <=3.10 (because subsequent calls will be cached and not
+ // allocate memory).
+
+ GCDisabledGuard no_gc;
+ Py_XDECREF(PyThreadState_GetFrame(PyThreadState_GET()));
+#endif
+}
+
+void PythonState::operator<<(const PyThreadState *const tstate) noexcept
+{
+ this->_context.steal(tstate->context);
+#if GREENLET_USE_CFRAME
+ /*
+ IMPORTANT: ``cframe`` is a pointer into the STACK. Thus, because
+ the call to ``slp_switch()`` changes the contents of the stack,
+ you cannot read from ``ts_current->cframe`` after that call and
+ necessarily get the same values you get from reading it here.
+ Anything you need to restore from now to then must be saved in a
+ global/threadlocal variable (because we can't use stack
+ variables here either). For things that need to persist across
+ the switch, use `will_switch_from`.
+ */
+ this->cframe = tstate->cframe;
+ #if !GREENLET_PY312
+ this->use_tracing = tstate->cframe->use_tracing;
+ #endif
+#endif // GREENLET_USE_CFRAME
+#if GREENLET_PY311
+ #if GREENLET_PY312
+ this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
+ this->c_recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining;
+ #else // not 312
+ this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining;
+ #endif // GREENLET_PY312
+ this->current_frame = tstate->cframe->current_frame;
+ this->datastack_chunk = tstate->datastack_chunk;
+ this->datastack_top = tstate->datastack_top;
+ this->datastack_limit = tstate->datastack_limit;
+
+ PyFrameObject *frame = PyThreadState_GetFrame((PyThreadState *)tstate);
+ Py_XDECREF(frame); // PyThreadState_GetFrame gives us a new
+ // reference.
+ this->_top_frame.steal(frame);
+ #if GREENLET_PY312
+ if (frame) {
+ this->_prev_frame = frame->f_frame->previous;
+ frame->f_frame->previous = nullptr;
+ }
+ #endif
+ #if GREENLET_PY312
+ this->trash_delete_nesting = tstate->trash.delete_nesting;
+ #else // not 312
+ this->trash_delete_nesting = tstate->trash_delete_nesting;
+ #endif // GREENLET_PY312
+#else // Not 311
+ this->recursion_depth = tstate->recursion_depth;
+ this->_top_frame.steal(tstate->frame);
+ this->trash_delete_nesting = tstate->trash_delete_nesting;
+#endif // GREENLET_PY311
+}
+
+
+void PythonState::operator>>(PyThreadState *const tstate) noexcept
+{
+ tstate->context = this->_context.relinquish_ownership();
+ /* Incrementing this value invalidates the contextvars cache,
+ which would otherwise remain valid across switches */
+ tstate->context_ver++;
+#if GREENLET_USE_CFRAME
+ tstate->cframe = this->cframe;
+ /*
+ If we were tracing, we need to keep tracing.
+ There should never be the possibility of hitting the
+ root_cframe here. See note above about why we can't
+ just copy this from ``origin->cframe->use_tracing``.
+ */
+ #if !GREENLET_PY312
+ tstate->cframe->use_tracing = this->use_tracing;
+ #endif
+#endif // GREENLET_USE_CFRAME
+#if GREENLET_PY311
+ #if GREENLET_PY312
+ tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth;
+ tstate->c_recursion_remaining = C_RECURSION_LIMIT - this->c_recursion_depth;
+ // We're just going to throw this object away anyway, go ahead and
+ // do it now.
+ PyFrameObject* frame = this->_top_frame.relinquish_ownership();
+ if (frame && frame->f_frame) {
+ frame->f_frame->previous = this->_prev_frame;
+ }
+ this->_prev_frame = nullptr;
+ #else // \/ 3.11
+ tstate->recursion_remaining = tstate->recursion_limit - this->recursion_depth;
+ #endif // GREENLET_PY312
+ tstate->cframe->current_frame = this->current_frame;
+ tstate->datastack_chunk = this->datastack_chunk;
+ tstate->datastack_top = this->datastack_top;
+ tstate->datastack_limit = this->datastack_limit;
+ this->_top_frame.relinquish_ownership();
+ #if GREENLET_PY312
+ tstate->trash.delete_nesting = this->trash_delete_nesting;
+ #else // not 3.12
+ tstate->trash_delete_nesting = this->trash_delete_nesting;
+ #endif // GREENLET_PY312
+#else // not 3.11
+ tstate->frame = this->_top_frame.relinquish_ownership();
+ tstate->recursion_depth = this->recursion_depth;
+ tstate->trash_delete_nesting = this->trash_delete_nesting;
+#endif // GREENLET_PY311
+}
+
+inline void PythonState::will_switch_from(PyThreadState *const origin_tstate) noexcept
+{
+#if GREENLET_USE_CFRAME && !GREENLET_PY312
+ // The weird thing is, we don't actually save this for an
+ // effect on the current greenlet, it's saved for an
+ // effect on the target greenlet. That is, we want
+ // continuity of this setting across the greenlet switch.
+ this->use_tracing = origin_tstate->cframe->use_tracing;
+#endif
+}
+
+void PythonState::set_initial_state(const PyThreadState* const tstate) noexcept
+{
+ this->_top_frame = nullptr;
+#if GREENLET_PY312
+ this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
+ // XXX: TODO: Comment from a reviewer:
+ // Should this be ``C_RECURSION_LIMIT - tstate->c_recursion_remaining``?
+ // But to me it looks more like that might not be the right
+ // initialization either?
+ this->c_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
+#elif GREENLET_PY311
+ this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining;
+#else
+ this->recursion_depth = tstate->recursion_depth;
+#endif
+}
+// TODO: Better state management about when we own the top frame.
+int PythonState::tp_traverse(visitproc visit, void* arg, bool own_top_frame) noexcept
+{
+ Py_VISIT(this->_context.borrow());
+ if (own_top_frame) {
+ Py_VISIT(this->_top_frame.borrow());
+ }
+ return 0;
+}
+
+void PythonState::tp_clear(bool own_top_frame) noexcept
+{
+ PythonStateContext::tp_clear();
+ // If we get here owning a frame,
+ // we got dealloc'd without being finished. We may or may not be
+ // in the same thread.
+ if (own_top_frame) {
+ this->_top_frame.CLEAR();
+ }
+}
+
+#if GREENLET_USE_CFRAME
+void PythonState::set_new_cframe(_PyCFrame& frame) noexcept
+{
+ frame = *PyThreadState_GET()->cframe;
+ /* Make the target greenlet refer to the stack value. */
+ this->cframe = &frame;
+ /*
+ And restore the link to the previous frame so this one gets
+ unliked appropriately.
+ */
+ this->cframe->previous = &PyThreadState_GET()->root_cframe;
+}
+#endif
+
+const PythonState::OwnedFrame& PythonState::top_frame() const noexcept
+{
+ return this->_top_frame;
+}
+
+void PythonState::did_finish(PyThreadState* tstate) noexcept
+{
+#if GREENLET_PY311
+ // See https://github.com/gevent/gevent/issues/1924 and
+ // https://github.com/python-greenlet/greenlet/issues/328. In
+ // short, Python 3.11 allocates memory for frames as a sort of
+ // linked list that's kept as part of PyThreadState in the
+ // ``datastack_chunk`` member and friends. These are saved and
+ // restored as part of switching greenlets.
+ //
+ // When we initially switch to a greenlet, we set those to NULL.
+ // That causes the frame management code to treat this like a
+ // brand new thread and start a fresh list of chunks, beginning
+ // with a new "root" chunk. As we make calls in this greenlet,
+ // those chunks get added, and as calls return, they get popped.
+ // But the frame code (pystate.c) is careful to make sure that the
+ // root chunk never gets popped.
+ //
+ // Thus, when a greenlet exits for the last time, there will be at
+ // least a single root chunk that we must be responsible for
+ // deallocating.
+ //
+ // The complex part is that these chunks are allocated and freed
+ // using ``_PyObject_VirtualAlloc``/``Free``. Those aren't public
+ // functions, and they aren't exported for linking. It so happens
+ // that we know they are just thin wrappers around the Arena
+ // allocator, so we can use that directly to deallocate in a
+ // compatible way.
+ //
+ // CAUTION: Check this implementation detail on every major version.
+ //
+ // It might be nice to be able to do this in our destructor, but
+ // can we be sure that no one else is using that memory? Plus, as
+ // described below, our pointers may not even be valid anymore. As
+ // a special case, there is one time that we know we can do this,
+ // and that's from the destructor of the associated UserGreenlet
+ // (NOT main greenlet)
+ PyObjectArenaAllocator alloc;
+ _PyStackChunk* chunk = nullptr;
+ if (tstate) {
+ // We really did finish, we can never be switched to again.
+ chunk = tstate->datastack_chunk;
+ // Unfortunately, we can't do much sanity checking. Our
+ // this->datastack_chunk pointer is out of date (evaluation may
+ // have popped down through it already) so we can't verify that
+ // we deallocate it. I don't think we can even check datastack_top
+ // for the same reason.
+
+ PyObject_GetArenaAllocator(&alloc);
+ tstate->datastack_chunk = nullptr;
+ tstate->datastack_limit = nullptr;
+ tstate->datastack_top = nullptr;
+
+ }
+ else if (this->datastack_chunk) {
+ // The UserGreenlet (NOT the main greenlet!) is being deallocated. If we're
+ // still holding a stack chunk, it's garbage because we know
+ // we can never switch back to let cPython clean it up.
+ // Because the last time we got switched away from, and we
+ // haven't run since then, we know our chain is valid and can
+ // be dealloced.
+ chunk = this->datastack_chunk;
+ PyObject_GetArenaAllocator(&alloc);
+ }
+
+ if (alloc.free && chunk) {
+ // In case the arena mechanism has been torn down already.
+ while (chunk) {
+ _PyStackChunk *prev = chunk->previous;
+ chunk->previous = nullptr;
+ alloc.free(alloc.ctx, chunk, chunk->size);
+ chunk = prev;
+ }
+ }
+
+ this->datastack_chunk = nullptr;
+ this->datastack_limit = nullptr;
+ this->datastack_top = nullptr;
+#endif
+}
+
+
+}; // namespace greenlet
+
+#endif // GREENLET_PYTHON_STATE_CPP
diff --git a/venv/lib/python3.10/site-packages/greenlet/TStackState.cpp b/venv/lib/python3.10/site-packages/greenlet/TStackState.cpp
new file mode 100644
index 000000000..5ce1ea192
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/TStackState.cpp
@@ -0,0 +1,232 @@
+#ifndef GREENLET_STACK_STATE_CPP
+#define GREENLET_STACK_STATE_CPP
+
+#include "greenlet_greenlet.hpp"
+
+namespace greenlet {
+
+#ifdef GREENLET_USE_STDIO
+#include
+using std::cerr;
+using std::endl;
+
+std::ostream& operator<<(std::ostream& os, const StackState& s)
+{
+ os << "StackState(stack_start=" << (void*)s._stack_start
+ << ", stack_stop=" << (void*)s.stack_stop
+ << ", stack_copy=" << (void*)s.stack_copy
+ << ", stack_saved=" << s._stack_saved
+ << ", stack_prev=" << s.stack_prev
+ << ", addr=" << &s
+ << ")";
+ return os;
+}
+#endif
+
+StackState::StackState(void* mark, StackState& current)
+ : _stack_start(nullptr),
+ stack_stop((char*)mark),
+ stack_copy(nullptr),
+ _stack_saved(0),
+ /* Skip a dying greenlet */
+ stack_prev(current._stack_start
+ ? ¤t
+ : current.stack_prev)
+{
+}
+
+StackState::StackState()
+ : _stack_start(nullptr),
+ stack_stop(nullptr),
+ stack_copy(nullptr),
+ _stack_saved(0),
+ stack_prev(nullptr)
+{
+}
+
+StackState::StackState(const StackState& other)
+// can't use a delegating constructor because of
+// MSVC for Python 2.7
+ : _stack_start(nullptr),
+ stack_stop(nullptr),
+ stack_copy(nullptr),
+ _stack_saved(0),
+ stack_prev(nullptr)
+{
+ this->operator=(other);
+}
+
+StackState& StackState::operator=(const StackState& other)
+{
+ if (&other == this) {
+ return *this;
+ }
+ if (other._stack_saved) {
+ throw std::runtime_error("Refusing to steal memory.");
+ }
+
+ //If we have memory allocated, dispose of it
+ this->free_stack_copy();
+
+ this->_stack_start = other._stack_start;
+ this->stack_stop = other.stack_stop;
+ this->stack_copy = other.stack_copy;
+ this->_stack_saved = other._stack_saved;
+ this->stack_prev = other.stack_prev;
+ return *this;
+}
+
+inline void StackState::free_stack_copy() noexcept
+{
+ PyMem_Free(this->stack_copy);
+ this->stack_copy = nullptr;
+ this->_stack_saved = 0;
+}
+
+inline void StackState::copy_heap_to_stack(const StackState& current) noexcept
+{
+
+ /* Restore the heap copy back into the C stack */
+ if (this->_stack_saved != 0) {
+ memcpy(this->_stack_start, this->stack_copy, this->_stack_saved);
+ this->free_stack_copy();
+ }
+ StackState* owner = const_cast(¤t);
+ if (!owner->_stack_start) {
+ owner = owner->stack_prev; /* greenlet is dying, skip it */
+ }
+ while (owner && owner->stack_stop <= this->stack_stop) {
+ // cerr << "\tOwner: " << owner << endl;
+ owner = owner->stack_prev; /* find greenlet with more stack */
+ }
+ this->stack_prev = owner;
+ // cerr << "\tFinished with: " << *this << endl;
+}
+
+inline int StackState::copy_stack_to_heap_up_to(const char* const stop) noexcept
+{
+ /* Save more of g's stack into the heap -- at least up to 'stop'
+ g->stack_stop |________|
+ | |
+ | __ stop . . . . .
+ | | ==> . .
+ |________| _______
+ | | | |
+ | | | |
+ g->stack_start | | |_______| g->stack_copy
+ */
+ intptr_t sz1 = this->_stack_saved;
+ intptr_t sz2 = stop - this->_stack_start;
+ assert(this->_stack_start);
+ if (sz2 > sz1) {
+ char* c = (char*)PyMem_Realloc(this->stack_copy, sz2);
+ if (!c) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ memcpy(c + sz1, this->_stack_start + sz1, sz2 - sz1);
+ this->stack_copy = c;
+ this->_stack_saved = sz2;
+ }
+ return 0;
+}
+
+inline int StackState::copy_stack_to_heap(char* const stackref,
+ const StackState& current) noexcept
+{
+ /* must free all the C stack up to target_stop */
+ const char* const target_stop = this->stack_stop;
+
+ StackState* owner = const_cast(¤t);
+ assert(owner->_stack_saved == 0); // everything is present on the stack
+ if (!owner->_stack_start) {
+ owner = owner->stack_prev; /* not saved if dying */
+ }
+ else {
+ owner->_stack_start = stackref;
+ }
+
+ while (owner->stack_stop < target_stop) {
+ /* ts_current is entierely within the area to free */
+ if (owner->copy_stack_to_heap_up_to(owner->stack_stop)) {
+ return -1; /* XXX */
+ }
+ owner = owner->stack_prev;
+ }
+ if (owner != this) {
+ if (owner->copy_stack_to_heap_up_to(target_stop)) {
+ return -1; /* XXX */
+ }
+ }
+ return 0;
+}
+
+inline bool StackState::started() const noexcept
+{
+ return this->stack_stop != nullptr;
+}
+
+inline bool StackState::main() const noexcept
+{
+ return this->stack_stop == (char*)-1;
+}
+
+inline bool StackState::active() const noexcept
+{
+ return this->_stack_start != nullptr;
+}
+
+inline void StackState::set_active() noexcept
+{
+ assert(this->_stack_start == nullptr);
+ this->_stack_start = (char*)1;
+}
+
+inline void StackState::set_inactive() noexcept
+{
+ this->_stack_start = nullptr;
+ // XXX: What if we still have memory out there?
+ // That case is actually triggered by
+ // test_issue251_issue252_explicit_reference_not_collectable (greenlet.tests.test_leaks.TestLeaks)
+ // and
+ // test_issue251_issue252_need_to_collect_in_background
+ // (greenlet.tests.test_leaks.TestLeaks)
+ //
+ // Those objects never get deallocated, so the destructor never
+ // runs.
+ // It *seems* safe to clean up the memory here?
+ if (this->_stack_saved) {
+ this->free_stack_copy();
+ }
+}
+
+inline intptr_t StackState::stack_saved() const noexcept
+{
+ return this->_stack_saved;
+}
+
+inline char* StackState::stack_start() const noexcept
+{
+ return this->_stack_start;
+}
+
+
+inline StackState StackState::make_main() noexcept
+{
+ StackState s;
+ s._stack_start = (char*)1;
+ s.stack_stop = (char*)-1;
+ return s;
+}
+
+StackState::~StackState()
+{
+ if (this->_stack_saved != 0) {
+ this->free_stack_copy();
+ }
+}
+
+
+}; // namespace greenlet
+
+#endif // GREENLET_STACK_STATE_CPP
diff --git a/venv/lib/python3.10/site-packages/greenlet/TThreadStateDestroy.cpp b/venv/lib/python3.10/site-packages/greenlet/TThreadStateDestroy.cpp
new file mode 100644
index 000000000..a149a1a4e
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/TThreadStateDestroy.cpp
@@ -0,0 +1,195 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+/**
+ * Implementation of the ThreadState destructors.
+ *
+ * Format with:
+ * clang-format -i --style=file src/greenlet/greenlet.c
+ *
+ *
+ * Fix missing braces with:
+ * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
+*/
+#ifndef T_THREADSTATE_DESTROY
+#define T_THREADSTATE_DESTROY
+
+#include "greenlet_greenlet.hpp"
+#include "greenlet_thread_state.hpp"
+#include "greenlet_thread_support.hpp"
+#include "greenlet_cpython_add_pending.hpp"
+#include "TGreenletGlobals.cpp"
+
+namespace greenlet {
+
+struct ThreadState_DestroyWithGIL
+{
+ ThreadState_DestroyWithGIL(ThreadState* state)
+ {
+ if (state && state->has_main_greenlet()) {
+ DestroyWithGIL(state);
+ }
+ }
+
+ static int
+ DestroyWithGIL(ThreadState* state)
+ {
+ // Holding the GIL.
+ // Passed a non-shared pointer to the actual thread state.
+ // state -> main greenlet
+ assert(state->has_main_greenlet());
+ PyGreenlet* main(state->borrow_main_greenlet());
+ // When we need to do cross-thread operations, we check this.
+ // A NULL value means the thread died some time ago.
+ // We do this here, rather than in a Python dealloc function
+ // for the greenlet, in case there's still a reference out
+ // there.
+ static_cast(main->pimpl)->thread_state(nullptr);
+
+ delete state; // Deleting this runs the destructor, DECREFs the main greenlet.
+ return 0;
+ }
+};
+
+
+
+struct ThreadState_DestroyNoGIL
+{
+ // ensure this is actually defined.
+ static_assert(GREENLET_BROKEN_PY_ADD_PENDING == 1 || GREENLET_BROKEN_PY_ADD_PENDING == 0,
+ "GREENLET_BROKEN_PY_ADD_PENDING not defined correctly.");
+
+#if GREENLET_BROKEN_PY_ADD_PENDING
+ static int _push_pending_call(struct _pending_calls *pending,
+ int (*func)(void *), void *arg)
+ {
+ int i = pending->last;
+ int j = (i + 1) % NPENDINGCALLS;
+ if (j == pending->first) {
+ return -1; /* Queue full */
+ }
+ pending->calls[i].func = func;
+ pending->calls[i].arg = arg;
+ pending->last = j;
+ return 0;
+ }
+
+ static int AddPendingCall(int (*func)(void *), void *arg)
+ {
+ _PyRuntimeState *runtime = &_PyRuntime;
+ if (!runtime) {
+ // obviously impossible
+ return 0;
+ }
+ struct _pending_calls *pending = &runtime->ceval.pending;
+ if (!pending->lock) {
+ return 0;
+ }
+ int result = 0;
+ PyThread_acquire_lock(pending->lock, WAIT_LOCK);
+ if (!pending->finishing) {
+ result = _push_pending_call(pending, func, arg);
+ }
+ PyThread_release_lock(pending->lock);
+ SIGNAL_PENDING_CALLS(&runtime->ceval);
+ return result;
+ }
+#else
+ // Python < 3.8 or >= 3.9
+ static int AddPendingCall(int (*func)(void*), void* arg)
+ {
+ return Py_AddPendingCall(func, arg);
+ }
+#endif
+
+ ThreadState_DestroyNoGIL(ThreadState* state)
+ {
+ // We are *NOT* holding the GIL. Our thread is in the middle
+ // of its death throes and the Python thread state is already
+ // gone so we can't use most Python APIs. One that is safe is
+ // ``Py_AddPendingCall``, unless the interpreter itself has
+ // been torn down. There is a limited number of calls that can
+ // be queued: 32 (NPENDINGCALLS) in CPython 3.10, so we
+ // coalesce these calls using our own queue.
+ if (state && state->has_main_greenlet()) {
+ // mark the thread as dead ASAP.
+ // this is racy! If we try to throw or switch to a
+ // greenlet from this thread from some other thread before
+ // we clear the state pointer, it won't realize the state
+ // is dead which can crash the process.
+ PyGreenlet* p = state->borrow_main_greenlet();
+ assert(p->pimpl->thread_state() == state || p->pimpl->thread_state() == nullptr);
+ static_cast(p->pimpl)->thread_state(nullptr);
+ }
+
+ // NOTE: Because we're not holding the GIL here, some other
+ // Python thread could run and call ``os.fork()``, which would
+ // be bad if that happenend while we are holding the cleanup
+ // lock (it wouldn't function in the child process).
+ // Make a best effort to try to keep the duration we hold the
+ // lock short.
+ // TODO: On platforms that support it, use ``pthread_atfork`` to
+ // drop this lock.
+ LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
+
+ if (state && state->has_main_greenlet()) {
+ // Because we don't have the GIL, this is a race condition.
+ if (!PyInterpreterState_Head()) {
+ // We have to leak the thread state, if the
+ // interpreter has shut down when we're getting
+ // deallocated, we can't run the cleanup code that
+ // deleting it would imply.
+ return;
+ }
+
+ mod_globs->queue_to_destroy(state);
+ if (mod_globs->thread_states_to_destroy.size() == 1) {
+ // We added the first item to the queue. We need to schedule
+ // the cleanup.
+ int result = ThreadState_DestroyNoGIL::AddPendingCall(
+ ThreadState_DestroyNoGIL::DestroyQueueWithGIL,
+ NULL);
+ if (result < 0) {
+ // Hmm, what can we do here?
+ fprintf(stderr,
+ "greenlet: WARNING: failed in call to Py_AddPendingCall; "
+ "expect a memory leak.\n");
+ }
+ }
+ }
+ }
+
+ static int
+ DestroyQueueWithGIL(void* UNUSED(arg))
+ {
+ // We're holding the GIL here, so no Python code should be able to
+ // run to call ``os.fork()``.
+ while (1) {
+ ThreadState* to_destroy;
+ {
+ LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
+ if (mod_globs->thread_states_to_destroy.empty()) {
+ break;
+ }
+ to_destroy = mod_globs->take_next_to_destroy();
+ }
+ // Drop the lock while we do the actual deletion.
+ ThreadState_DestroyWithGIL::DestroyWithGIL(to_destroy);
+ }
+ return 0;
+ }
+
+};
+
+}; // namespace greenlet
+
+// The intent when GET_THREAD_STATE() is needed multiple times in a
+// function is to take a reference to its return value in a local
+// variable, to avoid the thread-local indirection. On some platforms
+// (macOS), accessing a thread-local involves a function call (plus an
+// initial function call in each function that uses a thread local);
+// in contrast, static volatile variables are at some pre-computed
+// offset.
+typedef greenlet::ThreadStateCreator ThreadStateCreator;
+static thread_local ThreadStateCreator g_thread_state_global;
+#define GET_THREAD_STATE() g_thread_state_global
+
+#endif //T_THREADSTATE_DESTROY
diff --git a/venv/lib/python3.10/site-packages/greenlet/TUserGreenlet.cpp b/venv/lib/python3.10/site-packages/greenlet/TUserGreenlet.cpp
new file mode 100644
index 000000000..975b29cce
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/TUserGreenlet.cpp
@@ -0,0 +1,663 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+/**
+ * Implementation of greenlet::UserGreenlet.
+ *
+ * Format with:
+ * clang-format -i --style=file src/greenlet/greenlet.c
+ *
+ *
+ * Fix missing braces with:
+ * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
+*/
+
+#include "greenlet_internal.hpp"
+#include "greenlet_greenlet.hpp"
+#include "greenlet_thread_state.hpp"
+#include "TThreadStateDestroy.cpp"
+
+
+namespace greenlet {
+using greenlet::refs::BorrowedMainGreenlet;
+greenlet::PythonAllocator UserGreenlet::allocator;
+
+void* UserGreenlet::operator new(size_t UNUSED(count))
+{
+ return allocator.allocate(1);
+}
+
+
+void UserGreenlet::operator delete(void* ptr)
+{
+ return allocator.deallocate(static_cast(ptr),
+ 1);
+}
+
+
+UserGreenlet::UserGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent)
+ : Greenlet(p), _parent(the_parent)
+{
+ this->_self = p;
+}
+
+UserGreenlet::~UserGreenlet()
+{
+ // Python 3.11: If we don't clear out the raw frame datastack
+ // when deleting an unfinished greenlet,
+ // TestLeaks.test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_main fails.
+ this->python_state.did_finish(nullptr);
+ this->tp_clear();
+}
+
+BorrowedGreenlet
+UserGreenlet::self() const noexcept
+{
+ return this->_self;
+}
+
+
+
+const BorrowedMainGreenlet
+UserGreenlet::main_greenlet() const
+{
+ return this->_main_greenlet;
+}
+
+
+BorrowedMainGreenlet
+UserGreenlet::find_main_greenlet_in_lineage() const
+{
+ if (this->started()) {
+ assert(this->_main_greenlet);
+ return BorrowedMainGreenlet(this->_main_greenlet);
+ }
+
+ if (!this->_parent) {
+ /* garbage collected greenlet in chain */
+ // XXX: WHAT?
+ return BorrowedMainGreenlet(nullptr);
+ }
+
+ return this->_parent->find_main_greenlet_in_lineage();
+}
+
+
+/**
+ * CAUTION: This will allocate memory and may trigger garbage
+ * collection and arbitrary Python code.
+ */
+OwnedObject
+UserGreenlet::throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state)
+{
+ /* The dying greenlet cannot be a parent of ts_current
+ because the 'parent' field chain would hold a
+ reference */
+ UserGreenlet::ParentIsCurrentGuard with_current_parent(this, current_thread_state);
+
+ // We don't care about the return value, only whether an
+ // exception happened. Whether or not an exception happens,
+ // we need to restore the parent in case the greenlet gets
+ // resurrected.
+ return Greenlet::throw_GreenletExit_during_dealloc(current_thread_state);
+}
+
+ThreadState*
+UserGreenlet::thread_state() const noexcept
+{
+ // TODO: maybe make this throw, if the thread state isn't there?
+ // if (!this->main_greenlet) {
+ // throw std::runtime_error("No thread state"); // TODO: Better exception
+ // }
+ if (!this->_main_greenlet) {
+ return nullptr;
+ }
+ return this->_main_greenlet->thread_state();
+}
+
+
+bool
+UserGreenlet::was_running_in_dead_thread() const noexcept
+{
+ return this->_main_greenlet && !this->thread_state();
+}
+
+OwnedObject
+UserGreenlet::g_switch()
+{
+ assert(this->args() || PyErr_Occurred());
+
+ try {
+ this->check_switch_allowed();
+ }
+ catch (const PyErrOccurred&) {
+ this->release_args();
+ throw;
+ }
+
+ // Switching greenlets used to attempt to clean out ones that need
+ // deleted *if* we detected a thread switch. Should it still do
+ // that?
+ // An issue is that if we delete a greenlet from another thread,
+ // it gets queued to this thread, and ``kill_greenlet()`` switches
+ // back into the greenlet
+
+ /* find the real target by ignoring dead greenlets,
+ and if necessary starting a greenlet. */
+ switchstack_result_t err;
+ Greenlet* target = this;
+ // TODO: probably cleaner to handle the case where we do
+ // switch to ourself separately from the other cases.
+ // This can probably even further be simplified if we keep
+ // track of the switching_state we're going for and just call
+ // into g_switch() if it's not ourself. The main problem with that
+ // is that we would be using more stack space.
+ bool target_was_me = true;
+ bool was_initial_stub = false;
+ while (target) {
+ if (target->active()) {
+ if (!target_was_me) {
+ target->args() <<= this->args();
+ assert(!this->args());
+ }
+ err = target->g_switchstack();
+ break;
+ }
+ if (!target->started()) {
+ // We never encounter a main greenlet that's not started.
+ assert(!target->main());
+ UserGreenlet* real_target = static_cast(target);
+ assert(real_target);
+ void* dummymarker;
+ was_initial_stub = true;
+ if (!target_was_me) {
+ target->args() <<= this->args();
+ assert(!this->args());
+ }
+ try {
+ // This can only throw back to us while we're
+ // still in this greenlet. Once the new greenlet
+ // is bootstrapped, it has its own exception state.
+ err = real_target->g_initialstub(&dummymarker);
+ }
+ catch (const PyErrOccurred&) {
+ this->release_args();
+ throw;
+ }
+ catch (const GreenletStartedWhileInPython&) {
+ // The greenlet was started sometime before this
+ // greenlet actually switched to it, i.e.,
+ // "concurrent" calls to switch() or throw().
+ // We need to retry the switch.
+ // Note that the current greenlet has been reset
+ // to this one (or we wouldn't be running!)
+ continue;
+ }
+ break;
+ }
+
+ target = target->parent();
+ target_was_me = false;
+ }
+ // The ``this`` pointer and all other stack or register based
+ // variables are invalid now, at least where things succeed
+ // above.
+ // But this one, probably not so much? It's not clear if it's
+ // safe to throw an exception at this point.
+
+ if (err.status < 0) {
+ // If we get here, either g_initialstub()
+ // failed, or g_switchstack() failed. Either one of those
+ // cases SHOULD leave us in the original greenlet with a valid
+ // stack.
+ return this->on_switchstack_or_initialstub_failure(target, err, target_was_me, was_initial_stub);
+ }
+
+ // err.the_new_current_greenlet would be the same as ``target``,
+ // if target wasn't probably corrupt.
+ return err.the_new_current_greenlet->g_switch_finish(err);
+}
+
+
+
+Greenlet::switchstack_result_t
+UserGreenlet::g_initialstub(void* mark)
+{
+ OwnedObject run;
+
+ // We need to grab a reference to the current switch arguments
+ // in case we're entered concurrently during the call to
+ // GetAttr() and have to try again.
+ // We'll restore them when we return in that case.
+ // Scope them tightly to avoid ref leaks.
+ {
+ SwitchingArgs args(this->args());
+
+ /* save exception in case getattr clears it */
+ PyErrPieces saved;
+
+ /*
+ self.run is the object to call in the new greenlet.
+ This could run arbitrary python code and switch greenlets!
+ */
+ run = this->_self.PyRequireAttr(mod_globs->str_run);
+ /* restore saved exception */
+ saved.PyErrRestore();
+
+
+ /* recheck that it's safe to switch in case greenlet reparented anywhere above */
+ this->check_switch_allowed();
+
+ /* by the time we got here another start could happen elsewhere,
+ * that means it should now be a regular switch.
+ * This can happen if the Python code is a subclass that implements
+ * __getattribute__ or __getattr__, or makes ``run`` a descriptor;
+ * all of those can run arbitrary code that switches back into
+ * this greenlet.
+ */
+ if (this->stack_state.started()) {
+ // the successful switch cleared these out, we need to
+ // restore our version. They will be copied on up to the
+ // next target.
+ assert(!this->args());
+ this->args() <<= args;
+ throw GreenletStartedWhileInPython();
+ }
+ }
+
+ // Sweet, if we got here, we have the go-ahead and will switch
+ // greenlets.
+ // Nothing we do from here on out should allow for a thread or
+ // greenlet switch: No arbitrary calls to Python, including
+ // decref'ing
+
+#if GREENLET_USE_CFRAME
+ /* OK, we need it, we're about to switch greenlets, save the state. */
+ /*
+ See green_new(). This is a stack-allocated variable used
+ while *self* is in PyObject_Call().
+ We want to defer copying the state info until we're sure
+ we need it and are in a stable place to do so.
+ */
+ _PyCFrame trace_info;
+
+ this->python_state.set_new_cframe(trace_info);
+#endif
+ /* start the greenlet */
+ ThreadState& thread_state = GET_THREAD_STATE().state();
+ this->stack_state = StackState(mark,
+ thread_state.borrow_current()->stack_state);
+ this->python_state.set_initial_state(PyThreadState_GET());
+ this->exception_state.clear();
+ this->_main_greenlet = thread_state.get_main_greenlet();
+
+ /* perform the initial switch */
+ switchstack_result_t err = this->g_switchstack();
+ /* returns twice!
+ The 1st time with ``err == 1``: we are in the new greenlet.
+ This one owns a greenlet that used to be current.
+ The 2nd time with ``err <= 0``: back in the caller's
+ greenlet; this happens if the child finishes or switches
+ explicitly to us. Either way, the ``err`` variable is
+ created twice at the same memory location, but possibly
+ having different ``origin`` values. Note that it's not
+ constructed for the second time until the switch actually happens.
+ */
+ if (err.status == 1) {
+ // In the new greenlet.
+
+ // This never returns! Calling inner_bootstrap steals
+ // the contents of our run object within this stack frame, so
+ // it is not valid to do anything with it.
+ try {
+ this->inner_bootstrap(err.origin_greenlet.relinquish_ownership(),
+ run.relinquish_ownership());
+ }
+ // Getting a C++ exception here isn't good. It's probably a
+ // bug in the underlying greenlet, meaning it's probably a
+ // C++ extension. We're going to abort anyway, but try to
+ // display some nice information if possible.
+ //
+ // The catching is tested by
+ // ``test_cpp.CPPTests.test_unhandled_exception_in_greenlet_aborts``.
+ //
+ // PyErrOccurred can theoretically be thrown by
+ // inner_bootstrap() -> g_switch_finish(), but that should
+ // never make it back to here. It is a std::exception and
+ // would be caught if it is.
+ catch (const std::exception& e) {
+ std::string base = "greenlet: Unhandled C++ exception: ";
+ base += e.what();
+ Py_FatalError(base.c_str());
+ }
+ catch (...) {
+ // Some compilers/runtimes use exceptions internally.
+ // It appears that GCC on Linux with libstdc++ throws an
+ // exception internally at process shutdown time to unwind
+ // stacks and clean up resources. Depending on exactly
+ // where we are when the process exits, that could result
+ // in an unknown exception getting here. If we
+ // Py_FatalError() or abort() here, we interfere with
+ // orderly process shutdown. Throwing the exception on up
+ // is the right thing to do.
+ //
+ // gevent's ``examples/dns_mass_resolve.py`` demonstrates this.
+#ifndef NDEBUG
+ fprintf(stderr,
+ "greenlet: inner_bootstrap threw unknown exception; "
+ "is the process terminating?\n");
+#endif
+ throw;
+ }
+ Py_FatalError("greenlet: inner_bootstrap returned with no exception.\n");
+ }
+
+
+ // In contrast, notice that we're keeping the origin greenlet
+ // around as an owned reference; we need it to call the trace
+ // function for the switch back into the parent. It was only
+ // captured at the time the switch actually happened, though,
+ // so we haven't been keeping an extra reference around this
+ // whole time.
+
+ /* back in the parent */
+ if (err.status < 0) {
+ /* start failed badly, restore greenlet state */
+ this->stack_state = StackState();
+ this->_main_greenlet.CLEAR();
+ // CAUTION: This may run arbitrary Python code.
+ run.CLEAR(); // inner_bootstrap didn't run, we own the reference.
+ }
+
+ // In the success case, the spawned code (inner_bootstrap) will
+ // take care of decrefing this, so we relinquish ownership so as
+ // to not double-decref.
+
+ run.relinquish_ownership();
+
+ return err;
+}
+
+
+void
+UserGreenlet::inner_bootstrap(PyGreenlet* origin_greenlet, PyObject* run)
+{
+ // The arguments here would be another great place for move.
+ // As it is, we take them as a reference so that when we clear
+ // them we clear what's on the stack above us. Do that NOW, and
+ // without using a C++ RAII object,
+ // so there's no way that exiting the parent frame can clear it,
+ // or we clear it unexpectedly. This arises in the context of the
+ // interpreter shutting down. See https://github.com/python-greenlet/greenlet/issues/325
+ //PyObject* run = _run.relinquish_ownership();
+
+ /* in the new greenlet */
+ assert(this->thread_state()->borrow_current() == this->_self);
+ // C++ exceptions cannot propagate to the parent greenlet from
+ // here. (TODO: Do we need a catch(...) clause, perhaps on the
+ // function itself? ALl we could do is terminate the program.)
+ // NOTE: On 32-bit Windows, the call chain is extremely
+ // important here in ways that are subtle, having to do with
+ // the depth of the SEH list. The call to restore it MUST NOT
+ // add a new SEH handler to the list, or we'll restore it to
+ // the wrong thing.
+ this->thread_state()->restore_exception_state();
+ /* stack variables from above are no good and also will not unwind! */
+ // EXCEPT: That can't be true, we access run, among others, here.
+
+ this->stack_state.set_active(); /* running */
+
+ // We're about to possibly run Python code again, which
+ // could switch back/away to/from us, so we need to grab the
+ // arguments locally.
+ SwitchingArgs args;
+ args <<= this->args();
+ assert(!this->args());
+
+ // XXX: We could clear this much earlier, right?
+ // Or would that introduce the possibility of running Python
+ // code when we don't want to?
+ // CAUTION: This may run arbitrary Python code.
+ this->_run_callable.CLEAR();
+
+
+ // The first switch we need to manually call the trace
+ // function here instead of in g_switch_finish, because we
+ // never return there.
+ if (OwnedObject tracefunc = this->thread_state()->get_tracefunc()) {
+ OwnedGreenlet trace_origin;
+ trace_origin = origin_greenlet;
+ try {
+ g_calltrace(tracefunc,
+ args ? mod_globs->event_switch : mod_globs->event_throw,
+ trace_origin,
+ this->_self);
+ }
+ catch (const PyErrOccurred&) {
+ /* Turn trace errors into switch throws */
+ args.CLEAR();
+ }
+ }
+
+ // We no longer need the origin, it was only here for
+ // tracing.
+ // We may never actually exit this stack frame so we need
+ // to explicitly clear it.
+ // This could run Python code and switch.
+ Py_CLEAR(origin_greenlet);
+
+ OwnedObject result;
+ if (!args) {
+ /* pending exception */
+ result = NULL;
+ }
+ else {
+ /* call g.run(*args, **kwargs) */
+ // This could result in further switches
+ try {
+ //result = run.PyCall(args.args(), args.kwargs());
+ // CAUTION: Just invoking this, before the function even
+ // runs, may cause memory allocations, which may trigger
+ // GC, which may run arbitrary Python code.
+ result = OwnedObject::consuming(PyObject_Call(run, args.args().borrow(), args.kwargs().borrow()));
+ }
+ catch (...) {
+ // Unhandled C++ exception!
+
+ // If we declare ourselves as noexcept, if we don't catch
+ // this here, most platforms will just abort() the
+ // process. But on 64-bit Windows with older versions of
+ // the C runtime, this can actually corrupt memory and
+ // just return. We see this when compiling with the
+ // Windows 7.0 SDK targeting Windows Server 2008, but not
+ // when using the Appveyor Visual Studio 2019 image. So
+ // this currently only affects Python 2.7 on Windows 64.
+ // That is, the tests pass and the runtime aborts
+ // everywhere else.
+ //
+ // However, if we catch it and try to continue with a
+ // Python error, then all Windows 64 bit platforms corrupt
+ // memory. So all we can do is manually abort, hopefully
+ // with a good error message. (Note that the above was
+ // tested WITHOUT the `/EHr` switch being used at compile
+ // time, so MSVC may have "optimized" out important
+ // checking. Using that switch, we may be in a better
+ // place in terms of memory corruption.) But sometimes it
+ // can't be caught here at all, which is confusing but not
+ // terribly surprising; so again, the G_NOEXCEPT_WIN32
+ // plus "/EHr".
+ //
+ // Hopefully the basic C stdlib is still functional enough
+ // for us to at least print an error.
+ //
+ // It gets more complicated than that, though, on some
+ // platforms, specifically at least Linux/gcc/libstdc++. They use
+ // an exception to unwind the stack when a background
+ // thread exits. (See comments about noexcept.) So this
+ // may not actually represent anything untoward. On those
+ // platforms we allow throws of this to propagate, or
+ // attempt to anyway.
+# if defined(WIN32) || defined(_WIN32)
+ Py_FatalError(
+ "greenlet: Unhandled C++ exception from a greenlet run function. "
+ "Because memory is likely corrupted, terminating process.");
+ std::abort();
+#else
+ throw;
+#endif
+ }
+ }
+ // These lines may run arbitrary code
+ args.CLEAR();
+ Py_CLEAR(run);
+
+ if (!result
+ && mod_globs->PyExc_GreenletExit.PyExceptionMatches()
+ && (this->args())) {
+ // This can happen, for example, if our only reference
+ // goes away after we switch back to the parent.
+ // See test_dealloc_switch_args_not_lost
+ PyErrPieces clear_error;
+ result <<= this->args();
+ result = single_result(result);
+ }
+ this->release_args();
+ this->python_state.did_finish(PyThreadState_GET());
+
+ result = g_handle_exit(result);
+ assert(this->thread_state()->borrow_current() == this->_self);
+
+ /* jump back to parent */
+ this->stack_state.set_inactive(); /* dead */
+
+
+ // TODO: Can we decref some things here? Release our main greenlet
+ // and maybe parent?
+ for (Greenlet* parent = this->_parent;
+ parent;
+ parent = parent->parent()) {
+ // We need to somewhere consume a reference to
+ // the result; in most cases we'll never have control
+ // back in this stack frame again. Calling
+ // green_switch actually adds another reference!
+ // This would probably be clearer with a specific API
+ // to hand results to the parent.
+ parent->args() <<= result;
+ assert(!result);
+ // The parent greenlet now owns the result; in the
+ // typical case we'll never get back here to assign to
+ // result and thus release the reference.
+ try {
+ result = parent->g_switch();
+ }
+ catch (const PyErrOccurred&) {
+ // Ignore, keep passing the error on up.
+ }
+
+ /* Return here means switch to parent failed,
+ * in which case we throw *current* exception
+ * to the next parent in chain.
+ */
+ assert(!result);
+ }
+ /* We ran out of parents, cannot continue */
+ PyErr_WriteUnraisable(this->self().borrow_o());
+ Py_FatalError("greenlet: ran out of parent greenlets while propagating exception; "
+ "cannot continue");
+ std::abort();
+}
+
+void
+UserGreenlet::run(const BorrowedObject nrun)
+{
+ if (this->started()) {
+ throw AttributeError(
+ "run cannot be set "
+ "after the start of the greenlet");
+ }
+ this->_run_callable = nrun;
+}
+
+const OwnedGreenlet
+UserGreenlet::parent() const
+{
+ return this->_parent;
+}
+
+void
+UserGreenlet::parent(const BorrowedObject raw_new_parent)
+{
+ if (!raw_new_parent) {
+ throw AttributeError("can't delete attribute");
+ }
+
+ BorrowedMainGreenlet main_greenlet_of_new_parent;
+ BorrowedGreenlet new_parent(raw_new_parent.borrow()); // could
+ // throw
+ // TypeError!
+ for (BorrowedGreenlet p = new_parent; p; p = p->parent()) {
+ if (p == this->_self) {
+ throw ValueError("cyclic parent chain");
+ }
+ main_greenlet_of_new_parent = p->main_greenlet();
+ }
+
+ if (!main_greenlet_of_new_parent) {
+ throw ValueError("parent must not be garbage collected");
+ }
+
+ if (this->started()
+ && this->_main_greenlet != main_greenlet_of_new_parent) {
+ throw ValueError("parent cannot be on a different thread");
+ }
+
+ this->_parent = new_parent;
+}
+
+void
+UserGreenlet::murder_in_place()
+{
+ this->_main_greenlet.CLEAR();
+ Greenlet::murder_in_place();
+}
+
+bool
+UserGreenlet::belongs_to_thread(const ThreadState* thread_state) const
+{
+ return Greenlet::belongs_to_thread(thread_state) && this->_main_greenlet == thread_state->borrow_main_greenlet();
+}
+
+
+int
+UserGreenlet::tp_traverse(visitproc visit, void* arg)
+{
+ Py_VISIT(this->_parent.borrow_o());
+ Py_VISIT(this->_main_greenlet.borrow_o());
+ Py_VISIT(this->_run_callable.borrow_o());
+
+ return Greenlet::tp_traverse(visit, arg);
+}
+
+int
+UserGreenlet::tp_clear()
+{
+ Greenlet::tp_clear();
+ this->_parent.CLEAR();
+ this->_main_greenlet.CLEAR();
+ this->_run_callable.CLEAR();
+ return 0;
+}
+
+UserGreenlet::ParentIsCurrentGuard::ParentIsCurrentGuard(UserGreenlet* p,
+ const ThreadState& thread_state)
+ : oldparent(p->_parent),
+ greenlet(p)
+{
+ p->_parent = thread_state.get_current();
+}
+
+UserGreenlet::ParentIsCurrentGuard::~ParentIsCurrentGuard()
+{
+ this->greenlet->_parent = oldparent;
+ oldparent.CLEAR();
+}
+
+}; //namespace greenlet
diff --git a/venv/lib/python3.10/site-packages/greenlet/__init__.py b/venv/lib/python3.10/site-packages/greenlet/__init__.py
new file mode 100644
index 000000000..dc8038e71
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/__init__.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+"""
+The root of the greenlet package.
+"""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+__all__ = [
+ '__version__',
+ '_C_API',
+
+ 'GreenletExit',
+ 'error',
+
+ 'getcurrent',
+ 'greenlet',
+
+ 'gettrace',
+ 'settrace',
+]
+
+# pylint:disable=no-name-in-module
+
+###
+# Metadata
+###
+__version__ = '3.0.1'
+from ._greenlet import _C_API # pylint:disable=no-name-in-module
+
+###
+# Exceptions
+###
+from ._greenlet import GreenletExit
+from ._greenlet import error
+
+###
+# greenlets
+###
+from ._greenlet import getcurrent
+from ._greenlet import greenlet
+
+###
+# tracing
+###
+try:
+ from ._greenlet import gettrace
+ from ._greenlet import settrace
+except ImportError:
+ # Tracing wasn't supported.
+ # XXX: The option to disable it was removed in 1.0,
+ # so this branch should be dead code.
+ pass
+
+###
+# Constants
+# These constants aren't documented and aren't recommended.
+# In 1.0, USE_GC and USE_TRACING are always true, and USE_CONTEXT_VARS
+# is the same as ``sys.version_info[:2] >= 3.7``
+###
+from ._greenlet import GREENLET_USE_CONTEXT_VARS # pylint:disable=unused-import
+from ._greenlet import GREENLET_USE_GC # pylint:disable=unused-import
+from ._greenlet import GREENLET_USE_TRACING # pylint:disable=unused-import
+
+# Controlling the use of the gc module. Provisional API for this greenlet
+# implementation in 2.0.
+from ._greenlet import CLOCKS_PER_SEC # pylint:disable=unused-import
+from ._greenlet import enable_optional_cleanup # pylint:disable=unused-import
+from ._greenlet import get_clocks_used_doing_optional_cleanup # pylint:disable=unused-import
+
+# Other APIS in the _greenlet module are for test support.
diff --git a/venv/lib/python3.10/site-packages/greenlet/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/greenlet/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 000000000..e0e2ee4b7
Binary files /dev/null and b/venv/lib/python3.10/site-packages/greenlet/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/greenlet/_greenlet.cpython-310-x86_64-linux-gnu.so b/venv/lib/python3.10/site-packages/greenlet/_greenlet.cpython-310-x86_64-linux-gnu.so
new file mode 100755
index 000000000..315dbffb9
Binary files /dev/null and b/venv/lib/python3.10/site-packages/greenlet/_greenlet.cpython-310-x86_64-linux-gnu.so differ
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet.cpp b/venv/lib/python3.10/site-packages/greenlet/greenlet.cpp
new file mode 100644
index 000000000..505749805
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet.cpp
@@ -0,0 +1,1493 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+/* Format with:
+ * clang-format -i --style=file src/greenlet/greenlet.c
+ *
+ *
+ * Fix missing braces with:
+ * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
+*/
+#include
+#include
+#include
+#include
+
+
+#define PY_SSIZE_T_CLEAN
+#include
+#include "structmember.h" // PyMemberDef
+
+#include "greenlet_internal.hpp"
+// Code after this point can assume access to things declared in stdint.h,
+// including the fixed-width types. This goes for the platform-specific switch functions
+// as well.
+#include "greenlet_refs.hpp"
+#include "greenlet_slp_switch.hpp"
+#include "greenlet_thread_state.hpp"
+#include "greenlet_thread_support.hpp"
+#include "greenlet_greenlet.hpp"
+
+#include "TGreenletGlobals.cpp"
+#include "TThreadStateDestroy.cpp"
+#include "TGreenlet.cpp"
+#include "TMainGreenlet.cpp"
+#include "TUserGreenlet.cpp"
+#include "TBrokenGreenlet.cpp"
+#include "TExceptionState.cpp"
+#include "TPythonState.cpp"
+#include "TStackState.cpp"
+
+
+using greenlet::LockGuard;
+using greenlet::LockInitError;
+using greenlet::PyErrOccurred;
+using greenlet::Require;
+
+using greenlet::g_handle_exit;
+using greenlet::single_result;
+
+using greenlet::Greenlet;
+using greenlet::UserGreenlet;
+using greenlet::MainGreenlet;
+using greenlet::BrokenGreenlet;
+using greenlet::ThreadState;
+using greenlet::PythonState;
+
+
+
+// ******* Implementation of things from included files
+template
+greenlet::refs::_BorrowedGreenlet& greenlet::refs::_BorrowedGreenlet::operator=(const greenlet::refs::BorrowedObject& other)
+{
+ this->_set_raw_pointer(static_cast(other));
+ return *this;
+}
+
+template
+inline greenlet::refs::_BorrowedGreenlet::operator Greenlet*() const noexcept
+{
+ if (!this->p) {
+ return nullptr;
+ }
+ return reinterpret_cast(this->p)->pimpl;
+}
+
+template
+greenlet::refs::_BorrowedGreenlet::_BorrowedGreenlet(const BorrowedObject& p)
+ : BorrowedReference(nullptr)
+{
+
+ this->_set_raw_pointer(p.borrow());
+}
+
+template
+inline greenlet::refs::_OwnedGreenlet::operator Greenlet*() const noexcept
+{
+ if (!this->p) {
+ return nullptr;
+ }
+ return reinterpret_cast(this->p)->pimpl;
+}
+
+
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wmissing-field-initializers"
+# pragma clang diagnostic ignored "-Wwritable-strings"
+#elif defined(__GNUC__)
+# pragma GCC diagnostic push
+// warning: ISO C++ forbids converting a string constant to ‘char*’
+// (The python APIs aren't const correct and accept writable char*)
+# pragma GCC diagnostic ignored "-Wwrite-strings"
+#endif
+
+
+/***********************************************************
+
+A PyGreenlet is a range of C stack addresses that must be
+saved and restored in such a way that the full range of the
+stack contains valid data when we switch to it.
+
+Stack layout for a greenlet:
+
+ | ^^^ |
+ | older data |
+ | |
+ stack_stop . |_______________|
+ . | |
+ . | greenlet data |
+ . | in stack |
+ . * |_______________| . . _____________ stack_copy + stack_saved
+ . | | | |
+ . | data | |greenlet data|
+ . | unrelated | | saved |
+ . | to | | in heap |
+ stack_start . | this | . . |_____________| stack_copy
+ | greenlet |
+ | |
+ | newer data |
+ | vvv |
+
+
+Note that a greenlet's stack data is typically partly at its correct
+place in the stack, and partly saved away in the heap, but always in
+the above configuration: two blocks, the more recent one in the heap
+and the older one still in the stack (either block may be empty).
+
+Greenlets are chained: each points to the previous greenlet, which is
+the one that owns the data currently in the C stack above my
+stack_stop. The currently running greenlet is the first element of
+this chain. The main (initial) greenlet is the last one. Greenlets
+whose stack is entirely in the heap can be skipped from the chain.
+
+The chain is not related to execution order, but only to the order
+in which bits of C stack happen to belong to greenlets at a particular
+point in time.
+
+The main greenlet doesn't have a stack_stop: it is responsible for the
+complete rest of the C stack, and we don't know where it begins. We
+use (char*) -1, the largest possible address.
+
+States:
+ stack_stop == NULL && stack_start == NULL: did not start yet
+ stack_stop != NULL && stack_start == NULL: already finished
+ stack_stop != NULL && stack_start != NULL: active
+
+The running greenlet's stack_start is undefined but not NULL.
+
+ ***********************************************************/
+
+static PyGreenlet*
+green_create_main(ThreadState* state)
+{
+ PyGreenlet* gmain;
+
+ /* create the main greenlet for this thread */
+ gmain = (PyGreenlet*)PyType_GenericAlloc(&PyGreenlet_Type, 0);
+ if (gmain == NULL) {
+ Py_FatalError("green_create_main failed to alloc");
+ return NULL;
+ }
+ new MainGreenlet(gmain, state);
+
+ assert(Py_REFCNT(gmain) == 1);
+ return gmain;
+}
+
+
+
+/***********************************************************/
+
+/* Some functions must not be inlined:
+ * slp_restore_state, when inlined into slp_switch might cause
+ it to restore stack over its own local variables
+ * slp_save_state, when inlined would add its own local
+ variables to the saved stack, wasting space
+ * slp_switch, cannot be inlined for obvious reasons
+ * g_initialstub, when inlined would receive a pointer into its
+ own stack frame, leading to incomplete stack save/restore
+
+g_initialstub is a member function and declared virtual so that the
+compiler always calls it through a vtable.
+
+slp_save_state and slp_restore_state are also member functions. They
+are called from trampoline functions that themselves are declared as
+not eligible for inlining.
+*/
+
+extern "C" {
+static int GREENLET_NOINLINE(slp_save_state_trampoline)(char* stackref)
+{
+ return switching_thread_state->slp_save_state(stackref);
+}
+static void GREENLET_NOINLINE(slp_restore_state_trampoline)()
+{
+ switching_thread_state->slp_restore_state();
+}
+}
+
+
+/***********************************************************/
+
+static PyGreenlet*
+green_new(PyTypeObject* type, PyObject* UNUSED(args), PyObject* UNUSED(kwds))
+{
+ PyGreenlet* o =
+ (PyGreenlet*)PyBaseObject_Type.tp_new(type, mod_globs->empty_tuple, mod_globs->empty_dict);
+ if (o) {
+ new UserGreenlet(o, GET_THREAD_STATE().state().borrow_current());
+ assert(Py_REFCNT(o) == 1);
+ }
+ return o;
+}
+
+static PyGreenlet*
+green_unswitchable_new(PyTypeObject* type, PyObject* UNUSED(args), PyObject* UNUSED(kwds))
+{
+ PyGreenlet* o =
+ (PyGreenlet*)PyBaseObject_Type.tp_new(type, mod_globs->empty_tuple, mod_globs->empty_dict);
+ if (o) {
+ new BrokenGreenlet(o, GET_THREAD_STATE().state().borrow_current());
+ assert(Py_REFCNT(o) == 1);
+ }
+ return o;
+}
+
+static int
+green_setrun(BorrowedGreenlet self, BorrowedObject nrun, void* c);
+static int
+green_setparent(BorrowedGreenlet self, BorrowedObject nparent, void* c);
+
+static int
+green_init(BorrowedGreenlet self, BorrowedObject args, BorrowedObject kwargs)
+{
+ PyArgParseParam run;
+ PyArgParseParam nparent;
+ static const char* const kwlist[] = {
+ "run",
+ "parent",
+ NULL
+ };
+
+ // recall: The O specifier does NOT increase the reference count.
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwargs, "|OO:green", (char**)kwlist, &run, &nparent)) {
+ return -1;
+ }
+
+ if (run) {
+ if (green_setrun(self, run, NULL)) {
+ return -1;
+ }
+ }
+ if (nparent && !nparent.is_None()) {
+ return green_setparent(self, nparent, NULL);
+ }
+ return 0;
+}
+
+
+
+static int
+green_traverse(PyGreenlet* self, visitproc visit, void* arg)
+{
+ // We must only visit referenced objects, i.e. only objects
+ // Py_INCREF'ed by this greenlet (directly or indirectly):
+ //
+ // - stack_prev is not visited: holds previous stack pointer, but it's not
+ // referenced
+ // - frames are not visited as we don't strongly reference them;
+ // alive greenlets are not garbage collected
+ // anyway. This can be a problem, however, if this greenlet is
+ // never allowed to finish, and is referenced from the frame: we
+ // have an uncollectible cycle in that case. Note that the
+ // frame object itself is also frequently not even tracked by the GC
+ // starting with Python 3.7 (frames are allocated by the
+ // interpreter untracked, and only become tracked when their
+ // evaluation is finished if they have a refcount > 1). All of
+ // this is to say that we should probably strongly reference
+ // the frame object. Doing so, while always allowing GC on a
+ // greenlet, solves several leaks for us.
+
+ Py_VISIT(self->dict);
+ if (!self->pimpl) {
+ // Hmm. I have seen this at interpreter shutdown time,
+ // I think. That's very odd because this doesn't go away until
+ // we're ``green_dealloc()``, at which point we shouldn't be
+ // traversed anymore.
+ return 0;
+ }
+
+ return self->pimpl->tp_traverse(visit, arg);
+}
+
+static int
+green_is_gc(BorrowedGreenlet self)
+{
+ int result = 0;
+ /* Main greenlet can be garbage collected since it can only
+ become unreachable if the underlying thread exited.
+ Active greenlets --- including those that are suspended ---
+ cannot be garbage collected, however.
+ */
+ if (self->main() || !self->active()) {
+ result = 1;
+ }
+ // The main greenlet pointer will eventually go away after the thread dies.
+ if (self->was_running_in_dead_thread()) {
+ // Our thread is dead! We can never run again. Might as well
+ // GC us. Note that if a tuple containing only us and other
+ // immutable objects had been scanned before this, when we
+ // would have returned 0, the tuple will take itself out of GC
+ // tracking and never be investigated again. So that could
+ // result in both us and the tuple leaking due to an
+ // unreachable/uncollectible reference. The same goes for
+ // dictionaries.
+ //
+ // It's not a great idea to be changing our GC state on the
+ // fly.
+ result = 1;
+ }
+ return result;
+}
+
+
+static int
+green_clear(PyGreenlet* self)
+{
+ /* Greenlet is only cleared if it is about to be collected.
+ Since active greenlets are not garbage collectable, we can
+ be sure that, even if they are deallocated during clear,
+ nothing they reference is in unreachable or finalizers,
+ so even if it switches we are relatively safe. */
+ // XXX: Are we responsible for clearing weakrefs here?
+ Py_CLEAR(self->dict);
+ return self->pimpl->tp_clear();
+}
+
+/**
+ * Returns 0 on failure (the object was resurrected) or 1 on success.
+ **/
+static int
+_green_dealloc_kill_started_non_main_greenlet(BorrowedGreenlet self)
+{
+ /* Hacks hacks hacks copied from instance_dealloc() */
+ /* Temporarily resurrect the greenlet. */
+ assert(self.REFCNT() == 0);
+ Py_SET_REFCNT(self.borrow(), 1);
+ /* Save the current exception, if any. */
+ PyErrPieces saved_err;
+ try {
+ // BY THE TIME WE GET HERE, the state may actually be going
+ // away
+ // if we're shutting down the interpreter and freeing thread
+ // entries,
+ // this could result in freeing greenlets that were leaked. So
+ // we can't try to read the state.
+ self->deallocing_greenlet_in_thread(
+ self->thread_state()
+ ? static_cast(GET_THREAD_STATE())
+ : nullptr);
+ }
+ catch (const PyErrOccurred&) {
+ PyErr_WriteUnraisable(self.borrow_o());
+ /* XXX what else should we do? */
+ }
+ /* Check for no resurrection must be done while we keep
+ * our internal reference, otherwise PyFile_WriteObject
+ * causes recursion if using Py_INCREF/Py_DECREF
+ */
+ if (self.REFCNT() == 1 && self->active()) {
+ /* Not resurrected, but still not dead!
+ XXX what else should we do? we complain. */
+ PyObject* f = PySys_GetObject("stderr");
+ Py_INCREF(self.borrow_o()); /* leak! */
+ if (f != NULL) {
+ PyFile_WriteString("GreenletExit did not kill ", f);
+ PyFile_WriteObject(self.borrow_o(), f, 0);
+ PyFile_WriteString("\n", f);
+ }
+ }
+ /* Restore the saved exception. */
+ saved_err.PyErrRestore();
+ /* Undo the temporary resurrection; can't use DECREF here,
+ * it would cause a recursive call.
+ */
+ assert(self.REFCNT() > 0);
+
+ Py_ssize_t refcnt = self.REFCNT() - 1;
+ Py_SET_REFCNT(self.borrow_o(), refcnt);
+ if (refcnt != 0) {
+ /* Resurrected! */
+ _Py_NewReference(self.borrow_o());
+ Py_SET_REFCNT(self.borrow_o(), refcnt);
+ /* Better to use tp_finalizer slot (PEP 442)
+ * and call ``PyObject_CallFinalizerFromDealloc``,
+ * but that's only supported in Python 3.4+; see
+ * Modules/_io/iobase.c for an example.
+ *
+ * The following approach is copied from iobase.c in CPython 2.7.
+ * (along with much of this function in general). Here's their
+ * comment:
+ *
+ * When called from a heap type's dealloc, the type will be
+ * decref'ed on return (see e.g. subtype_dealloc in typeobject.c). */
+ if (PyType_HasFeature(self.TYPE(), Py_TPFLAGS_HEAPTYPE)) {
+ Py_INCREF(self.TYPE());
+ }
+
+ PyObject_GC_Track((PyObject*)self);
+
+ _Py_DEC_REFTOTAL;
+#ifdef COUNT_ALLOCS
+ --Py_TYPE(self)->tp_frees;
+ --Py_TYPE(self)->tp_allocs;
+#endif /* COUNT_ALLOCS */
+ return 0;
+ }
+ return 1;
+}
+
+
+static void
+green_dealloc(PyGreenlet* self)
+{
+ PyObject_GC_UnTrack(self);
+ BorrowedGreenlet me(self);
+ if (me->active()
+ && me->started()
+ && !me->main()) {
+ if (!_green_dealloc_kill_started_non_main_greenlet(me)) {
+ return;
+ }
+ }
+
+ if (self->weakreflist != NULL) {
+ PyObject_ClearWeakRefs((PyObject*)self);
+ }
+ Py_CLEAR(self->dict);
+
+ if (self->pimpl) {
+ // In case deleting this, which frees some memory,
+ // somehow winds up calling back into us. That's usually a
+ //bug in our code.
+ Greenlet* p = self->pimpl;
+ self->pimpl = nullptr;
+ delete p;
+ }
+ // and finally we're done. self is now invalid.
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+
+
+static OwnedObject
+throw_greenlet(BorrowedGreenlet self, PyErrPieces& err_pieces)
+{
+ PyObject* result = nullptr;
+ err_pieces.PyErrRestore();
+ assert(PyErr_Occurred());
+ if (self->started() && !self->active()) {
+ /* dead greenlet: turn GreenletExit into a regular return */
+ result = g_handle_exit(OwnedObject()).relinquish_ownership();
+ }
+ self->args() <<= result;
+
+ return single_result(self->g_switch());
+}
+
+
+
+PyDoc_STRVAR(
+ green_switch_doc,
+ "switch(*args, **kwargs)\n"
+ "\n"
+ "Switch execution to this greenlet.\n"
+ "\n"
+ "If this greenlet has never been run, then this greenlet\n"
+ "will be switched to using the body of ``self.run(*args, **kwargs)``.\n"
+ "\n"
+ "If the greenlet is active (has been run, but was switch()'ed\n"
+ "out before leaving its run function), then this greenlet will\n"
+ "be resumed and the return value to its switch call will be\n"
+ "None if no arguments are given, the given argument if one\n"
+ "argument is given, or the args tuple and keyword args dict if\n"
+ "multiple arguments are given.\n"
+ "\n"
+ "If the greenlet is dead, or is the current greenlet then this\n"
+ "function will simply return the arguments using the same rules as\n"
+ "above.\n");
+
+static PyObject*
+green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs)
+{
+ using greenlet::SwitchingArgs;
+ SwitchingArgs switch_args(OwnedObject::owning(args), OwnedObject::owning(kwargs));
+ self->pimpl->may_switch_away();
+ self->pimpl->args() <<= switch_args;
+
+ // If we're switching out of a greenlet, and that switch is the
+ // last thing the greenlet does, the greenlet ought to be able to
+ // go ahead and die at that point. Currently, someone else must
+ // manually switch back to the greenlet so that we "fall off the
+ // end" and can perform cleanup. You'd think we'd be able to
+ // figure out that this is happening using the frame's ``f_lasti``
+ // member, which is supposed to be an index into
+ // ``frame->f_code->co_code``, the bytecode string. However, in
+ // recent interpreters, ``f_lasti`` tends not to be updated thanks
+ // to things like the PREDICT() macros in ceval.c. So it doesn't
+ // really work to do that in many cases. For example, the Python
+ // code:
+ // def run():
+ // greenlet.getcurrent().parent.switch()
+ // produces bytecode of len 16, with the actual call to switch()
+ // being at index 10 (in Python 3.10). However, the reported
+ // ``f_lasti`` we actually see is...5! (Which happens to be the
+ // second byte of the CALL_METHOD op for ``getcurrent()``).
+
+ try {
+ //OwnedObject result = single_result(self->pimpl->g_switch());
+ OwnedObject result(single_result(self->pimpl->g_switch()));
+#ifndef NDEBUG
+ // Note that the current greenlet isn't necessarily self. If self
+ // finished, we went to one of its parents.
+ assert(!self->pimpl->args());
+
+ const BorrowedGreenlet& current = GET_THREAD_STATE().state().borrow_current();
+ // It's possible it's never been switched to.
+ assert(!current->args());
+#endif
+ PyObject* p = result.relinquish_ownership();
+
+ if (!p && !PyErr_Occurred()) {
+ // This shouldn't be happening anymore, so the asserts
+ // are there for debug builds. Non-debug builds
+ // crash "gracefully" in this case, although there is an
+ // argument to be made for killing the process in all
+ // cases --- for this to be the case, our switches
+ // probably nested in an incorrect way, so the state is
+ // suspicious. Nothing should be corrupt though, just
+ // confused at the Python level. Letting this propagate is
+ // probably good enough.
+ assert(p || PyErr_Occurred());
+ throw PyErrOccurred(
+ mod_globs->PyExc_GreenletError,
+ "Greenlet.switch() returned NULL without an exception set."
+ );
+ }
+ return p;
+ }
+ catch(const PyErrOccurred&) {
+ return nullptr;
+ }
+}
+
+PyDoc_STRVAR(
+ green_throw_doc,
+ "Switches execution to this greenlet, but immediately raises the\n"
+ "given exception in this greenlet. If no argument is provided, the "
+ "exception\n"
+ "defaults to `greenlet.GreenletExit`. The normal exception\n"
+ "propagation rules apply, as described for `switch`. Note that calling "
+ "this\n"
+ "method is almost equivalent to the following::\n"
+ "\n"
+ " def raiser():\n"
+ " raise typ, val, tb\n"
+ " g_raiser = greenlet(raiser, parent=g)\n"
+ " g_raiser.switch()\n"
+ "\n"
+ "except that this trick does not work for the\n"
+ "`greenlet.GreenletExit` exception, which would not propagate\n"
+ "from ``g_raiser`` to ``g``.\n");
+
+static PyObject*
+green_throw(PyGreenlet* self, PyObject* args)
+{
+ PyArgParseParam typ(mod_globs->PyExc_GreenletExit);
+ PyArgParseParam val;
+ PyArgParseParam tb;
+
+ if (!PyArg_ParseTuple(args, "|OOO:throw", &typ, &val, &tb)) {
+ return nullptr;
+ }
+
+ assert(typ.borrow() || val.borrow());
+
+ self->pimpl->may_switch_away();
+ try {
+ // Both normalizing the error and the actual throw_greenlet
+ // could throw PyErrOccurred.
+ PyErrPieces err_pieces(typ.borrow(), val.borrow(), tb.borrow());
+
+ return throw_greenlet(self, err_pieces).relinquish_ownership();
+ }
+ catch (const PyErrOccurred&) {
+ return nullptr;
+ }
+}
+
+static int
+green_bool(PyGreenlet* self)
+{
+ return self->pimpl->active();
+}
+
+/**
+ * CAUTION: Allocates memory, may run GC and arbitrary Python code.
+ */
+static PyObject*
+green_getdict(PyGreenlet* self, void* UNUSED(context))
+{
+ if (self->dict == NULL) {
+ self->dict = PyDict_New();
+ if (self->dict == NULL) {
+ return NULL;
+ }
+ }
+ Py_INCREF(self->dict);
+ return self->dict;
+}
+
+static int
+green_setdict(PyGreenlet* self, PyObject* val, void* UNUSED(context))
+{
+ PyObject* tmp;
+
+ if (val == NULL) {
+ PyErr_SetString(PyExc_TypeError, "__dict__ may not be deleted");
+ return -1;
+ }
+ if (!PyDict_Check(val)) {
+ PyErr_SetString(PyExc_TypeError, "__dict__ must be a dictionary");
+ return -1;
+ }
+ tmp = self->dict;
+ Py_INCREF(val);
+ self->dict = val;
+ Py_XDECREF(tmp);
+ return 0;
+}
+
+static bool
+_green_not_dead(BorrowedGreenlet self)
+{
+ // XXX: Where else should we do this?
+ // Probably on entry to most Python-facing functions?
+ if (self->was_running_in_dead_thread()) {
+ self->deactivate_and_free();
+ return false;
+ }
+ return self->active() || !self->started();
+}
+
+
+static PyObject*
+green_getdead(BorrowedGreenlet self, void* UNUSED(context))
+{
+ if (_green_not_dead(self)) {
+ Py_RETURN_FALSE;
+ }
+ else {
+ Py_RETURN_TRUE;
+ }
+}
+
+static PyObject*
+green_get_stack_saved(PyGreenlet* self, void* UNUSED(context))
+{
+ return PyLong_FromSsize_t(self->pimpl->stack_saved());
+}
+
+
+static PyObject*
+green_getrun(BorrowedGreenlet self, void* UNUSED(context))
+{
+ try {
+ OwnedObject result(self->run());
+ return result.relinquish_ownership();
+ }
+ catch(const PyErrOccurred&) {
+ return nullptr;
+ }
+}
+
+
+
+
+
+static int
+green_setrun(BorrowedGreenlet self, BorrowedObject nrun, void* UNUSED(context))
+{
+ try {
+ self->run(nrun);
+ return 0;
+ }
+ catch(const PyErrOccurred&) {
+ return -1;
+ }
+}
+
+static PyObject*
+green_getparent(BorrowedGreenlet self, void* UNUSED(context))
+{
+ return self->parent().acquire_or_None();
+}
+
+
+
+static int
+green_setparent(BorrowedGreenlet self, BorrowedObject nparent, void* UNUSED(context))
+{
+ try {
+ self->parent(nparent);
+ }
+ catch(const PyErrOccurred&) {
+ return -1;
+ }
+ return 0;
+}
+
+
+static PyObject*
+green_getcontext(const PyGreenlet* self, void* UNUSED(context))
+{
+ const Greenlet *const g = self->pimpl;
+ try {
+ OwnedObject result(g->context());
+ return result.relinquish_ownership();
+ }
+ catch(const PyErrOccurred&) {
+ return nullptr;
+ }
+}
+
+static int
+green_setcontext(BorrowedGreenlet self, PyObject* nctx, void* UNUSED(context))
+{
+ try {
+ self->context(nctx);
+ return 0;
+ }
+ catch(const PyErrOccurred&) {
+ return -1;
+ }
+}
+
+
+static PyObject*
+green_getframe(BorrowedGreenlet self, void* UNUSED(context))
+{
+ const PythonState::OwnedFrame& top_frame = self->top_frame();
+ return top_frame.acquire_or_None();
+}
+
+static PyObject*
+green_getstate(PyGreenlet* self)
+{
+ PyErr_Format(PyExc_TypeError,
+ "cannot serialize '%s' object",
+ Py_TYPE(self)->tp_name);
+ return nullptr;
+}
+
+static PyObject*
+green_repr(BorrowedGreenlet self)
+{
+ /*
+ Return a string like
+
+
+ The handling of greenlets across threads is not super good.
+ We mostly use the internal definitions of these terms, but they
+ generally should make sense to users as well.
+ */
+ PyObject* result;
+ int never_started = !self->started() && !self->active();
+
+ const char* const tp_name = Py_TYPE(self)->tp_name;
+
+ if (_green_not_dead(self)) {
+ /* XXX: The otid= is almost useless because you can't correlate it to
+ any thread identifier exposed to Python. We could use
+ PyThreadState_GET()->thread_id, but we'd need to save that in the
+ greenlet, or save the whole PyThreadState object itself.
+
+ As it stands, its only useful for identifying greenlets from the same thread.
+ */
+ const char* state_in_thread;
+ if (self->was_running_in_dead_thread()) {
+ // The thread it was running in is dead!
+ // This can happen, especially at interpreter shut down.
+ // It complicates debugging output because it may be
+ // impossible to access the current thread state at that
+ // time. Thus, don't access the current thread state.
+ state_in_thread = " (thread exited)";
+ }
+ else {
+ state_in_thread = GET_THREAD_STATE().state().is_current(self)
+ ? " current"
+ : (self->started() ? " suspended" : "");
+ }
+ result = PyUnicode_FromFormat(
+ "<%s object at %p (otid=%p)%s%s%s%s>",
+ tp_name,
+ self.borrow_o(),
+ self->thread_state(),
+ state_in_thread,
+ self->active() ? " active" : "",
+ never_started ? " pending" : " started",
+ self->main() ? " main" : ""
+ );
+ }
+ else {
+ result = PyUnicode_FromFormat(
+ "<%s object at %p (otid=%p) %sdead>",
+ tp_name,
+ self.borrow_o(),
+ self->thread_state(),
+ self->was_running_in_dead_thread()
+ ? "(thread exited) "
+ : ""
+ );
+ }
+
+ return result;
+}
+
+/*****************************************************************************
+ * C interface
+ *
+ * These are exported using the CObject API
+ */
+extern "C" {
+static PyGreenlet*
+PyGreenlet_GetCurrent(void)
+{
+ return GET_THREAD_STATE().state().get_current().relinquish_ownership();
+}
+
+static int
+PyGreenlet_SetParent(PyGreenlet* g, PyGreenlet* nparent)
+{
+ return green_setparent((PyGreenlet*)g, (PyObject*)nparent, NULL);
+}
+
+static PyGreenlet*
+PyGreenlet_New(PyObject* run, PyGreenlet* parent)
+{
+ using greenlet::refs::NewDictReference;
+ // In the past, we didn't use green_new and green_init, but that
+ // was a maintenance issue because we duplicated code. This way is
+ // much safer, but slightly slower. If that's a problem, we could
+ // refactor green_init to separate argument parsing from initialization.
+ OwnedGreenlet g = OwnedGreenlet::consuming(green_new(&PyGreenlet_Type, nullptr, nullptr));
+ if (!g) {
+ return NULL;
+ }
+
+ try {
+ NewDictReference kwargs;
+ if (run) {
+ kwargs.SetItem(mod_globs->str_run, run);
+ }
+ if (parent) {
+ kwargs.SetItem("parent", (PyObject*)parent);
+ }
+
+ Require(green_init(g, mod_globs->empty_tuple, kwargs));
+ }
+ catch (const PyErrOccurred&) {
+ return nullptr;
+ }
+
+ return g.relinquish_ownership();
+}
+
+static PyObject*
+PyGreenlet_Switch(PyGreenlet* self, PyObject* args, PyObject* kwargs)
+{
+ if (!PyGreenlet_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+
+ if (args == NULL) {
+ args = mod_globs->empty_tuple;
+ }
+
+ if (kwargs == NULL || !PyDict_Check(kwargs)) {
+ kwargs = NULL;
+ }
+
+ return green_switch(self, args, kwargs);
+}
+
+static PyObject*
+PyGreenlet_Throw(PyGreenlet* self, PyObject* typ, PyObject* val, PyObject* tb)
+{
+ if (!PyGreenlet_Check(self)) {
+ PyErr_BadArgument();
+ return nullptr;
+ }
+ try {
+ PyErrPieces err_pieces(typ, val, tb);
+ return throw_greenlet(self, err_pieces).relinquish_ownership();
+ }
+ catch (const PyErrOccurred&) {
+ return nullptr;
+ }
+}
+
+static int
+Extern_PyGreenlet_MAIN(PyGreenlet* self)
+{
+ if (!PyGreenlet_Check(self)) {
+ PyErr_BadArgument();
+ return -1;
+ }
+ return self->pimpl->main();
+}
+
+static int
+Extern_PyGreenlet_ACTIVE(PyGreenlet* self)
+{
+ if (!PyGreenlet_Check(self)) {
+ PyErr_BadArgument();
+ return -1;
+ }
+ return self->pimpl->active();
+}
+
+static int
+Extern_PyGreenlet_STARTED(PyGreenlet* self)
+{
+ if (!PyGreenlet_Check(self)) {
+ PyErr_BadArgument();
+ return -1;
+ }
+ return self->pimpl->started();
+}
+
+static PyGreenlet*
+Extern_PyGreenlet_GET_PARENT(PyGreenlet* self)
+{
+ if (!PyGreenlet_Check(self)) {
+ PyErr_BadArgument();
+ return NULL;
+ }
+ // This can return NULL even if there is no exception
+ return self->pimpl->parent().acquire();
+}
+} // extern C.
+
+/** End C API ****************************************************************/
+
+static PyMethodDef green_methods[] = {
+ {"switch",
+ reinterpret_cast(green_switch),
+ METH_VARARGS | METH_KEYWORDS,
+ green_switch_doc},
+ {"throw", (PyCFunction)green_throw, METH_VARARGS, green_throw_doc},
+ {"__getstate__", (PyCFunction)green_getstate, METH_NOARGS, NULL},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyGetSetDef green_getsets[] = {
+ /* name, getter, setter, doc, context pointer */
+ {"__dict__", (getter)green_getdict, (setter)green_setdict, /*XXX*/ NULL},
+ {"run", (getter)green_getrun, (setter)green_setrun, /*XXX*/ NULL},
+ {"parent", (getter)green_getparent, (setter)green_setparent, /*XXX*/ NULL},
+ {"gr_frame", (getter)green_getframe, NULL, /*XXX*/ NULL},
+ {"gr_context",
+ (getter)green_getcontext,
+ (setter)green_setcontext,
+ /*XXX*/ NULL},
+ {"dead", (getter)green_getdead, NULL, /*XXX*/ NULL},
+ {"_stack_saved", (getter)green_get_stack_saved, NULL, /*XXX*/ NULL},
+ {NULL}
+};
+
+static PyMemberDef green_members[] = {
+ {NULL}
+};
+
+static PyNumberMethods green_as_number = {
+ NULL, /* nb_add */
+ NULL, /* nb_subtract */
+ NULL, /* nb_multiply */
+ NULL, /* nb_remainder */
+ NULL, /* nb_divmod */
+ NULL, /* nb_power */
+ NULL, /* nb_negative */
+ NULL, /* nb_positive */
+ NULL, /* nb_absolute */
+ (inquiry)green_bool, /* nb_bool */
+};
+
+
+PyTypeObject PyGreenlet_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "greenlet.greenlet", /* tp_name */
+ sizeof(PyGreenlet), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)green_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)green_repr, /* tp_repr */
+ &green_as_number, /* tp_as _number*/
+ 0, /* tp_as _sequence*/
+ 0, /* tp_as _mapping*/
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer*/
+ G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "greenlet(run=None, parent=None) -> greenlet\n\n"
+ "Creates a new greenlet object (without running it).\n\n"
+ " - *run* -- The callable to invoke.\n"
+ " - *parent* -- The parent greenlet. The default is the current "
+ "greenlet.", /* tp_doc */
+ (traverseproc)green_traverse, /* tp_traverse */
+ (inquiry)green_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ offsetof(PyGreenlet, weakreflist), /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ green_methods, /* tp_methods */
+ green_members, /* tp_members */
+ green_getsets, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ offsetof(PyGreenlet, dict), /* tp_dictoffset */
+ (initproc)green_init, /* tp_init */
+ PyType_GenericAlloc, /* tp_alloc */
+ (newfunc)green_new, /* tp_new */
+ PyObject_GC_Del, /* tp_free */
+ (inquiry)green_is_gc, /* tp_is_gc */
+};
+
+
+
+static PyObject*
+green_unswitchable_getforce(PyGreenlet* self, void* UNUSED(context))
+{
+ BrokenGreenlet* broken = dynamic_cast(self->pimpl);
+ return PyBool_FromLong(broken->_force_switch_error);
+}
+
+static int
+green_unswitchable_setforce(PyGreenlet* self, BorrowedObject nforce, void* UNUSED(context))
+{
+ if (!nforce) {
+ PyErr_SetString(
+ PyExc_AttributeError,
+ "Cannot delete force_switch_error"
+ );
+ return -1;
+ }
+ BrokenGreenlet* broken = dynamic_cast(self->pimpl);
+ int is_true = PyObject_IsTrue(nforce);
+ if (is_true == -1) {
+ return -1;
+ }
+ broken->_force_switch_error = is_true;
+ return 0;
+}
+
+static PyObject*
+green_unswitchable_getforceslp(PyGreenlet* self, void* UNUSED(context))
+{
+ BrokenGreenlet* broken = dynamic_cast(self->pimpl);
+ return PyBool_FromLong(broken->_force_slp_switch_error);
+}
+
+static int
+green_unswitchable_setforceslp(PyGreenlet* self, BorrowedObject nforce, void* UNUSED(context))
+{
+ if (!nforce) {
+ PyErr_SetString(
+ PyExc_AttributeError,
+ "Cannot delete force_slp_switch_error"
+ );
+ return -1;
+ }
+ BrokenGreenlet* broken = dynamic_cast(self->pimpl);
+ int is_true = PyObject_IsTrue(nforce);
+ if (is_true == -1) {
+ return -1;
+ }
+ broken->_force_slp_switch_error = is_true;
+ return 0;
+}
+
+static PyGetSetDef green_unswitchable_getsets[] = {
+ /* name, getter, setter, doc, context pointer */
+ {"force_switch_error",
+ (getter)green_unswitchable_getforce,
+ (setter)green_unswitchable_setforce,
+ /*XXX*/ NULL},
+ {"force_slp_switch_error",
+ (getter)green_unswitchable_getforceslp,
+ (setter)green_unswitchable_setforceslp,
+ /*XXX*/ NULL},
+
+ {NULL}
+};
+
+PyTypeObject PyGreenletUnswitchable_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "greenlet._greenlet.UnswitchableGreenlet",
+ 0, /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)green_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as _number*/
+ 0, /* tp_as _sequence*/
+ 0, /* tp_as _mapping*/
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer*/
+ G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "Undocumented internal class", /* tp_doc */
+ (traverseproc)green_traverse, /* tp_traverse */
+ (inquiry)green_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ green_unswitchable_getsets, /* tp_getset */
+ &PyGreenlet_Type, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)green_init, /* tp_init */
+ PyType_GenericAlloc, /* tp_alloc */
+ (newfunc)green_unswitchable_new, /* tp_new */
+ PyObject_GC_Del, /* tp_free */
+ (inquiry)green_is_gc, /* tp_is_gc */
+};
+
+
+PyDoc_STRVAR(mod_getcurrent_doc,
+ "getcurrent() -> greenlet\n"
+ "\n"
+ "Returns the current greenlet (i.e. the one which called this "
+ "function).\n");
+
+static PyObject*
+mod_getcurrent(PyObject* UNUSED(module))
+{
+ return GET_THREAD_STATE().state().get_current().relinquish_ownership_o();
+}
+
+PyDoc_STRVAR(mod_settrace_doc,
+ "settrace(callback) -> object\n"
+ "\n"
+ "Sets a new tracing function and returns the previous one.\n");
+static PyObject*
+mod_settrace(PyObject* UNUSED(module), PyObject* args)
+{
+ PyArgParseParam tracefunc;
+ if (!PyArg_ParseTuple(args, "O", &tracefunc)) {
+ return NULL;
+ }
+ ThreadState& state = GET_THREAD_STATE();
+ OwnedObject previous = state.get_tracefunc();
+ if (!previous) {
+ previous = Py_None;
+ }
+
+ state.set_tracefunc(tracefunc);
+
+ return previous.relinquish_ownership();
+}
+
+PyDoc_STRVAR(mod_gettrace_doc,
+ "gettrace() -> object\n"
+ "\n"
+ "Returns the currently set tracing function, or None.\n");
+
+static PyObject*
+mod_gettrace(PyObject* UNUSED(module))
+{
+ OwnedObject tracefunc = GET_THREAD_STATE().state().get_tracefunc();
+ if (!tracefunc) {
+ tracefunc = Py_None;
+ }
+ return tracefunc.relinquish_ownership();
+}
+
+PyDoc_STRVAR(mod_set_thread_local_doc,
+ "set_thread_local(key, value) -> None\n"
+ "\n"
+ "Set a value in the current thread-local dictionary. Debbuging only.\n");
+
+static PyObject*
+mod_set_thread_local(PyObject* UNUSED(module), PyObject* args)
+{
+ PyArgParseParam key;
+ PyArgParseParam value;
+ PyObject* result = NULL;
+
+ if (PyArg_UnpackTuple(args, "set_thread_local", 2, 2, &key, &value)) {
+ if(PyDict_SetItem(
+ PyThreadState_GetDict(), // borrow
+ key,
+ value) == 0 ) {
+ // success
+ Py_INCREF(Py_None);
+ result = Py_None;
+ }
+ }
+ return result;
+}
+
+PyDoc_STRVAR(mod_get_pending_cleanup_count_doc,
+ "get_pending_cleanup_count() -> Integer\n"
+ "\n"
+ "Get the number of greenlet cleanup operations pending. Testing only.\n");
+
+
+static PyObject*
+mod_get_pending_cleanup_count(PyObject* UNUSED(module))
+{
+ LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
+ return PyLong_FromSize_t(mod_globs->thread_states_to_destroy.size());
+}
+
+PyDoc_STRVAR(mod_get_total_main_greenlets_doc,
+ "get_total_main_greenlets() -> Integer\n"
+ "\n"
+ "Quickly return the number of main greenlets that exist. Testing only.\n");
+
+static PyObject*
+mod_get_total_main_greenlets(PyObject* UNUSED(module))
+{
+ return PyLong_FromSize_t(G_TOTAL_MAIN_GREENLETS);
+}
+
+PyDoc_STRVAR(mod_get_clocks_used_doing_optional_cleanup_doc,
+ "get_clocks_used_doing_optional_cleanup() -> Integer\n"
+ "\n"
+ "Get the number of clock ticks the program has used doing optional "
+ "greenlet cleanup.\n"
+ "Beginning in greenlet 2.0, greenlet tries to find and dispose of greenlets\n"
+ "that leaked after a thread exited. This requires invoking Python's garbage collector,\n"
+ "which may have a performance cost proportional to the number of live objects.\n"
+ "This function returns the amount of processor time\n"
+ "greenlet has used to do this. In programs that run with very large amounts of live\n"
+ "objects, this metric can be used to decide whether the cost of doing this cleanup\n"
+ "is worth the memory leak being corrected. If not, you can disable the cleanup\n"
+ "using ``enable_optional_cleanup(False)``.\n"
+ "The units are arbitrary and can only be compared to themselves (similarly to ``time.clock()``);\n"
+ "for example, to see how it scales with your heap. You can attempt to convert them into seconds\n"
+ "by dividing by the value of CLOCKS_PER_SEC."
+ "If cleanup has been disabled, returns None."
+ "\n"
+ "This is an implementation specific, provisional API. It may be changed or removed\n"
+ "in the future.\n"
+ ".. versionadded:: 2.0"
+ );
+static PyObject*
+mod_get_clocks_used_doing_optional_cleanup(PyObject* UNUSED(module))
+{
+ std::clock_t& clocks = ThreadState::clocks_used_doing_gc();
+
+ if (clocks == std::clock_t(-1)) {
+ Py_RETURN_NONE;
+ }
+ // This might not actually work on some implementations; clock_t
+ // is an opaque type.
+ return PyLong_FromSsize_t(clocks);
+}
+
+PyDoc_STRVAR(mod_enable_optional_cleanup_doc,
+ "mod_enable_optional_cleanup(bool) -> None\n"
+ "\n"
+ "Enable or disable optional cleanup operations.\n"
+ "See ``get_clocks_used_doing_optional_cleanup()`` for details.\n"
+ );
+static PyObject*
+mod_enable_optional_cleanup(PyObject* UNUSED(module), PyObject* flag)
+{
+ int is_true = PyObject_IsTrue(flag);
+ if (is_true == -1) {
+ return nullptr;
+ }
+
+ std::clock_t& clocks = ThreadState::clocks_used_doing_gc();
+ if (is_true) {
+ // If we already have a value, we don't want to lose it.
+ if (clocks == std::clock_t(-1)) {
+ clocks = 0;
+ }
+ }
+ else {
+ clocks = std::clock_t(-1);
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(mod_get_tstate_trash_delete_nesting_doc,
+ "get_tstate_trash_delete_nesting() -> Integer\n"
+ "\n"
+ "Return the 'trash can' nesting level. Testing only.\n");
+static PyObject*
+mod_get_tstate_trash_delete_nesting(PyObject* UNUSED(module))
+{
+ PyThreadState* tstate = PyThreadState_GET();
+
+#if GREENLET_PY312
+ return PyLong_FromLong(tstate->trash.delete_nesting);
+#else
+ return PyLong_FromLong(tstate->trash_delete_nesting);
+#endif
+}
+
+static PyMethodDef GreenMethods[] = {
+ {"getcurrent",
+ (PyCFunction)mod_getcurrent,
+ METH_NOARGS,
+ mod_getcurrent_doc},
+ {"settrace", (PyCFunction)mod_settrace, METH_VARARGS, mod_settrace_doc},
+ {"gettrace", (PyCFunction)mod_gettrace, METH_NOARGS, mod_gettrace_doc},
+ {"set_thread_local", (PyCFunction)mod_set_thread_local, METH_VARARGS, mod_set_thread_local_doc},
+ {"get_pending_cleanup_count", (PyCFunction)mod_get_pending_cleanup_count, METH_NOARGS, mod_get_pending_cleanup_count_doc},
+ {"get_total_main_greenlets", (PyCFunction)mod_get_total_main_greenlets, METH_NOARGS, mod_get_total_main_greenlets_doc},
+ {"get_clocks_used_doing_optional_cleanup", (PyCFunction)mod_get_clocks_used_doing_optional_cleanup, METH_NOARGS, mod_get_clocks_used_doing_optional_cleanup_doc},
+ {"enable_optional_cleanup", (PyCFunction)mod_enable_optional_cleanup, METH_O, mod_enable_optional_cleanup_doc},
+ {"get_tstate_trash_delete_nesting", (PyCFunction)mod_get_tstate_trash_delete_nesting, METH_NOARGS, mod_get_tstate_trash_delete_nesting_doc},
+ {NULL, NULL} /* Sentinel */
+};
+
+static const char* const copy_on_greentype[] = {
+ "getcurrent",
+ "error",
+ "GreenletExit",
+ "settrace",
+ "gettrace",
+ NULL
+};
+
+static struct PyModuleDef greenlet_module_def = {
+ PyModuleDef_HEAD_INIT,
+ "greenlet._greenlet",
+ NULL,
+ -1,
+ GreenMethods,
+};
+
+
+
+static PyObject*
+greenlet_internal_mod_init() noexcept
+{
+ static void* _PyGreenlet_API[PyGreenlet_API_pointers];
+
+ try {
+ CreatedModule m(greenlet_module_def);
+
+ Require(PyType_Ready(&PyGreenlet_Type));
+ Require(PyType_Ready(&PyGreenletUnswitchable_Type));
+
+ mod_globs = new greenlet::GreenletGlobals;
+ ThreadState::init();
+
+ m.PyAddObject("greenlet", PyGreenlet_Type);
+ m.PyAddObject("UnswitchableGreenlet", PyGreenletUnswitchable_Type);
+ m.PyAddObject("error", mod_globs->PyExc_GreenletError);
+ m.PyAddObject("GreenletExit", mod_globs->PyExc_GreenletExit);
+
+ m.PyAddObject("GREENLET_USE_GC", 1);
+ m.PyAddObject("GREENLET_USE_TRACING", 1);
+ m.PyAddObject("GREENLET_USE_CONTEXT_VARS", 1L);
+ m.PyAddObject("GREENLET_USE_STANDARD_THREADING", 1L);
+
+ OwnedObject clocks_per_sec = OwnedObject::consuming(PyLong_FromSsize_t(CLOCKS_PER_SEC));
+ m.PyAddObject("CLOCKS_PER_SEC", clocks_per_sec);
+
+ /* also publish module-level data as attributes of the greentype. */
+ // XXX: This is weird, and enables a strange pattern of
+ // confusing the class greenlet with the module greenlet; with
+ // the exception of (possibly) ``getcurrent()``, this
+ // shouldn't be encouraged so don't add new items here.
+ for (const char* const* p = copy_on_greentype; *p; p++) {
+ OwnedObject o = m.PyRequireAttr(*p);
+ PyDict_SetItemString(PyGreenlet_Type.tp_dict, *p, o.borrow());
+ }
+
+ /*
+ * Expose C API
+ */
+
+ /* types */
+ _PyGreenlet_API[PyGreenlet_Type_NUM] = (void*)&PyGreenlet_Type;
+
+ /* exceptions */
+ _PyGreenlet_API[PyExc_GreenletError_NUM] = (void*)mod_globs->PyExc_GreenletError;
+ _PyGreenlet_API[PyExc_GreenletExit_NUM] = (void*)mod_globs->PyExc_GreenletExit;
+
+ /* methods */
+ _PyGreenlet_API[PyGreenlet_New_NUM] = (void*)PyGreenlet_New;
+ _PyGreenlet_API[PyGreenlet_GetCurrent_NUM] = (void*)PyGreenlet_GetCurrent;
+ _PyGreenlet_API[PyGreenlet_Throw_NUM] = (void*)PyGreenlet_Throw;
+ _PyGreenlet_API[PyGreenlet_Switch_NUM] = (void*)PyGreenlet_Switch;
+ _PyGreenlet_API[PyGreenlet_SetParent_NUM] = (void*)PyGreenlet_SetParent;
+
+ /* Previously macros, but now need to be functions externally. */
+ _PyGreenlet_API[PyGreenlet_MAIN_NUM] = (void*)Extern_PyGreenlet_MAIN;
+ _PyGreenlet_API[PyGreenlet_STARTED_NUM] = (void*)Extern_PyGreenlet_STARTED;
+ _PyGreenlet_API[PyGreenlet_ACTIVE_NUM] = (void*)Extern_PyGreenlet_ACTIVE;
+ _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM] = (void*)Extern_PyGreenlet_GET_PARENT;
+
+ /* XXX: Note that our module name is ``greenlet._greenlet``, but for
+ backwards compatibility with existing C code, we need the _C_API to
+ be directly in greenlet.
+ */
+ const NewReference c_api_object(Require(
+ PyCapsule_New(
+ (void*)_PyGreenlet_API,
+ "greenlet._C_API",
+ NULL)));
+ m.PyAddObject("_C_API", c_api_object);
+ assert(c_api_object.REFCNT() == 2);
+
+ // cerr << "Sizes:"
+ // << "\n\tGreenlet : " << sizeof(Greenlet)
+ // << "\n\tUserGreenlet : " << sizeof(UserGreenlet)
+ // << "\n\tMainGreenlet : " << sizeof(MainGreenlet)
+ // << "\n\tExceptionState : " << sizeof(greenlet::ExceptionState)
+ // << "\n\tPythonState : " << sizeof(greenlet::PythonState)
+ // << "\n\tStackState : " << sizeof(greenlet::StackState)
+ // << "\n\tSwitchingArgs : " << sizeof(greenlet::SwitchingArgs)
+ // << "\n\tOwnedObject : " << sizeof(greenlet::refs::OwnedObject)
+ // << "\n\tBorrowedObject : " << sizeof(greenlet::refs::BorrowedObject)
+ // << "\n\tPyGreenlet : " << sizeof(PyGreenlet)
+ // << endl;
+
+ return m.borrow(); // But really it's the main reference.
+ }
+ catch (const LockInitError& e) {
+ PyErr_SetString(PyExc_MemoryError, e.what());
+ return NULL;
+ }
+ catch (const PyErrOccurred&) {
+ return NULL;
+ }
+
+}
+
+extern "C" {
+
+PyMODINIT_FUNC
+PyInit__greenlet(void)
+{
+ return greenlet_internal_mod_init();
+}
+
+}; // extern C
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#elif defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet.h b/venv/lib/python3.10/site-packages/greenlet/greenlet.h
new file mode 100644
index 000000000..d02a16e43
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet.h
@@ -0,0 +1,164 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+
+/* Greenlet object interface */
+
+#ifndef Py_GREENLETOBJECT_H
+#define Py_GREENLETOBJECT_H
+
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is deprecated and undocumented. It does not change. */
+#define GREENLET_VERSION "1.0.0"
+
+#ifndef GREENLET_MODULE
+#define implementation_ptr_t void*
+#endif
+
+typedef struct _greenlet {
+ PyObject_HEAD
+ PyObject* weakreflist;
+ PyObject* dict;
+ implementation_ptr_t pimpl;
+} PyGreenlet;
+
+#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
+
+
+/* C API functions */
+
+/* Total number of symbols that are exported */
+#define PyGreenlet_API_pointers 12
+
+#define PyGreenlet_Type_NUM 0
+#define PyExc_GreenletError_NUM 1
+#define PyExc_GreenletExit_NUM 2
+
+#define PyGreenlet_New_NUM 3
+#define PyGreenlet_GetCurrent_NUM 4
+#define PyGreenlet_Throw_NUM 5
+#define PyGreenlet_Switch_NUM 6
+#define PyGreenlet_SetParent_NUM 7
+
+#define PyGreenlet_MAIN_NUM 8
+#define PyGreenlet_STARTED_NUM 9
+#define PyGreenlet_ACTIVE_NUM 10
+#define PyGreenlet_GET_PARENT_NUM 11
+
+#ifndef GREENLET_MODULE
+/* This section is used by modules that uses the greenlet C API */
+static void** _PyGreenlet_API = NULL;
+
+# define PyGreenlet_Type \
+ (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
+
+# define PyExc_GreenletError \
+ ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
+
+# define PyExc_GreenletExit \
+ ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
+
+/*
+ * PyGreenlet_New(PyObject *args)
+ *
+ * greenlet.greenlet(run, parent=None)
+ */
+# define PyGreenlet_New \
+ (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
+ _PyGreenlet_API[PyGreenlet_New_NUM])
+
+/*
+ * PyGreenlet_GetCurrent(void)
+ *
+ * greenlet.getcurrent()
+ */
+# define PyGreenlet_GetCurrent \
+ (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
+
+/*
+ * PyGreenlet_Throw(
+ * PyGreenlet *greenlet,
+ * PyObject *typ,
+ * PyObject *val,
+ * PyObject *tb)
+ *
+ * g.throw(...)
+ */
+# define PyGreenlet_Throw \
+ (*(PyObject * (*)(PyGreenlet * self, \
+ PyObject * typ, \
+ PyObject * val, \
+ PyObject * tb)) \
+ _PyGreenlet_API[PyGreenlet_Throw_NUM])
+
+/*
+ * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
+ *
+ * g.switch(*args, **kwargs)
+ */
+# define PyGreenlet_Switch \
+ (*(PyObject * \
+ (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
+ _PyGreenlet_API[PyGreenlet_Switch_NUM])
+
+/*
+ * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
+ *
+ * g.parent = new_parent
+ */
+# define PyGreenlet_SetParent \
+ (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
+ _PyGreenlet_API[PyGreenlet_SetParent_NUM])
+
+/*
+ * PyGreenlet_GetParent(PyObject* greenlet)
+ *
+ * return greenlet.parent;
+ *
+ * This could return NULL even if there is no exception active.
+ * If it does not return NULL, you are responsible for decrementing the
+ * reference count.
+ */
+# define PyGreenlet_GetParent \
+ (*(PyGreenlet* (*)(PyGreenlet*)) \
+ _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
+
+/*
+ * deprecated, undocumented alias.
+ */
+# define PyGreenlet_GET_PARENT PyGreenlet_GetParent
+
+# define PyGreenlet_MAIN \
+ (*(int (*)(PyGreenlet*)) \
+ _PyGreenlet_API[PyGreenlet_MAIN_NUM])
+
+# define PyGreenlet_STARTED \
+ (*(int (*)(PyGreenlet*)) \
+ _PyGreenlet_API[PyGreenlet_STARTED_NUM])
+
+# define PyGreenlet_ACTIVE \
+ (*(int (*)(PyGreenlet*)) \
+ _PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
+
+
+
+
+/* Macro that imports greenlet and initializes C API */
+/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
+ keep the older definition to be sure older code that might have a copy of
+ the header still works. */
+# define PyGreenlet_Import() \
+ { \
+ _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
+ }
+
+#endif /* GREENLET_MODULE */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_GREENLETOBJECT_H */
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_allocator.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_allocator.hpp
new file mode 100644
index 000000000..b452f5444
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_allocator.hpp
@@ -0,0 +1,63 @@
+#ifndef GREENLET_ALLOCATOR_HPP
+#define GREENLET_ALLOCATOR_HPP
+
+#define PY_SSIZE_T_CLEAN
+#include
+#include
+#include "greenlet_compiler_compat.hpp"
+
+
+namespace greenlet
+{
+ // This allocator is stateless; all instances are identical.
+ // It can *ONLY* be used when we're sure we're holding the GIL
+ // (Python's allocators require the GIL).
+ template
+ struct PythonAllocator : public std::allocator {
+
+ PythonAllocator(const PythonAllocator& UNUSED(other))
+ : std::allocator()
+ {
+ }
+
+ PythonAllocator(const std::allocator other)
+ : std::allocator(other)
+ {}
+
+ template
+ PythonAllocator(const std::allocator& other)
+ : std::allocator(other)
+ {
+ }
+
+ PythonAllocator() : std::allocator() {}
+
+ T* allocate(size_t number_objects, const void* UNUSED(hint)=0)
+ {
+ void* p;
+ if (number_objects == 1)
+ p = PyObject_Malloc(sizeof(T));
+ else
+ p = PyMem_Malloc(sizeof(T) * number_objects);
+ return static_cast(p);
+ }
+
+ void deallocate(T* t, size_t n)
+ {
+ void* p = t;
+ if (n == 1) {
+ PyObject_Free(p);
+ }
+ else
+ PyMem_Free(p);
+ }
+ // This member is deprecated in C++17 and removed in C++20
+ template< class U >
+ struct rebind {
+ typedef PythonAllocator other;
+ };
+
+ };
+}
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_compiler_compat.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_compiler_compat.hpp
new file mode 100644
index 000000000..ee5bbdd20
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_compiler_compat.hpp
@@ -0,0 +1,95 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+#ifndef GREENLET_COMPILER_COMPAT_HPP
+#define GREENLET_COMPILER_COMPAT_HPP
+
+/**
+ * Definitions to aid with compatibility with different compilers.
+ *
+ * .. caution:: Use extreme care with noexcept.
+ * Some compilers and runtimes, specifically gcc/libgcc/libstdc++ on
+ * Linux, implement stack unwinding by throwing an uncatchable
+ * exception, one that specifically does not appear to be an active
+ * exception to the rest of the runtime. If this happens while we're in a noexcept function,
+ * we have violated our dynamic exception contract, and so the runtime
+ * will call std::terminate(), which kills the process with the
+ * unhelpful message "terminate called without an active exception".
+ *
+ * This has happened in this scenario: A background thread is running
+ * a greenlet that has made a native call and released the GIL.
+ * Meanwhile, the main thread finishes and starts shutting down the
+ * interpreter. When the background thread is scheduled again and
+ * attempts to obtain the GIL, it notices that the interpreter is
+ * exiting and calls ``pthread_exit()``. This in turn starts to unwind
+ * the stack by throwing that exception. But we had the ``PyCall``
+ * functions annotated as noexcept, so the runtime terminated us.
+ *
+ * #2 0x00007fab26fec2b7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6
+ * #3 0x00007fab26febb3c in __gxx_personality_v0 () from /lib/x86_64-linux-gnu/libstdc++.so.6
+ * #4 0x00007fab26f34de6 in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1
+ * #6 0x00007fab276a34c6 in __GI___pthread_unwind at ./nptl/unwind.c:130
+ * #7 0x00007fab2769bd3a in __do_cancel () at ../sysdeps/nptl/pthreadP.h:280
+ * #8 __GI___pthread_exit (value=value@entry=0x0) at ./nptl/pthread_exit.c:36
+ * #9 0x000000000052e567 in PyThread_exit_thread () at ../Python/thread_pthread.h:370
+ * #10 0x00000000004d60b5 in take_gil at ../Python/ceval_gil.h:224
+ * #11 0x00000000004d65f9 in PyEval_RestoreThread at ../Python/ceval.c:467
+ * #12 0x000000000060cce3 in setipaddr at ../Modules/socketmodule.c:1203
+ * #13 0x00000000006101cd in socket_gethostbyname
+ */
+
+#include
+
+# if defined(__clang__)
+# define G_FP_TMPL_STATIC static
+# else
+// GCC has no problem allowing static function pointers, but emits
+// tons of warnings about "whose type uses the anonymous namespace [-Wsubobject-linkage]"
+# define G_FP_TMPL_STATIC
+# endif
+
+# define G_NO_COPIES_OF_CLS(Cls) private: \
+ Cls(const Cls& other) = delete; \
+ Cls& operator=(const Cls& other) = delete
+
+# define G_NO_ASSIGNMENT_OF_CLS(Cls) private: \
+ Cls& operator=(const Cls& other) = delete
+
+# define G_NO_COPY_CONSTRUCTOR_OF_CLS(Cls) private: \
+ Cls(const Cls& other) = delete;
+
+
+// CAUTION: MSVC is stupidly picky:
+//
+// "The compiler ignores, without warning, any __declspec keywords
+// placed after * or & and in front of the variable identifier in a
+// declaration."
+// (https://docs.microsoft.com/en-us/cpp/cpp/declspec?view=msvc-160)
+//
+// So pointer return types must be handled differently (because of the
+// trailing *), or you get inscrutable compiler warnings like "error
+// C2059: syntax error: ''"
+//
+// In C++ 11, there is a standard syntax for attributes, and
+// GCC defines an attribute to use with this: [[gnu:noinline]].
+// In the future, this is expected to become standard.
+
+#if defined(__GNUC__) || defined(__clang__)
+/* We used to check for GCC 4+ or 3.4+, but those compilers are
+ laughably out of date. Just assume they support it. */
+# define GREENLET_NOINLINE(name) __attribute__((noinline)) name
+# define GREENLET_NOINLINE_P(rtype, name) rtype __attribute__((noinline)) name
+# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
+#elif defined(_MSC_VER)
+/* We used to check for && (_MSC_VER >= 1300) but that's also out of date. */
+# define GREENLET_NOINLINE(name) __declspec(noinline) name
+# define GREENLET_NOINLINE_P(rtype, name) __declspec(noinline) rtype name
+# define UNUSED(x) UNUSED_ ## x
+#endif
+
+#if defined(_MSC_VER)
+# define G_NOEXCEPT_WIN32 noexcept
+#else
+# define G_NOEXCEPT_WIN32
+#endif
+
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_cpython_add_pending.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_cpython_add_pending.hpp
new file mode 100644
index 000000000..0d28efd3d
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_cpython_add_pending.hpp
@@ -0,0 +1,172 @@
+#ifndef GREENLET_CPYTHON_ADD_PENDING_HPP
+#define GREENLET_CPYTHON_ADD_PENDING_HPP
+
+#if (PY_VERSION_HEX >= 0x30800A0 && PY_VERSION_HEX < 0x3090000) && !(defined(_WIN32) || defined(WIN32))
+// XXX: From Python 3.8a3 [1] up until Python 3.9a6 [2][3],
+// ``Py_AddPendingCall`` would try to produce a Python exception if
+// the interpreter was in the beginning of shutting down when this
+// function is called. However, ``Py_AddPendingCall`` doesn't require
+// the GIL, and we are absolutely not holding it when we make that
+// call. That means that trying to create the Python exception is
+// using the C API in an undefined state; here the C API detects this
+// and aborts the process with an error ("Fatal Python error: Python
+// memory allocator called without holding the GIL": Add ->
+// PyErr_SetString -> PyUnicode_New -> PyObject_Malloc). This arises
+// (obviously) in multi-threaded programs and happens if one thread is
+// exiting and cleaning up its thread-local data while the other
+// thread is trying to shut down the interpreter. A crash on shutdown
+// is still a crash and could result in data loss (e.g., daemon
+// threads are still running, pending signal handlers may be present,
+// buffers may not be flushed, there may be __del__ that need run,
+// etc), so we have to work around it.
+//
+// Of course, we can (and do) check for whether the interpreter is
+// shutting down before calling ``Py_AddPendingCall``, but that's a
+// race condition since we don't hold the GIL, and so we may not
+// actually get the right answer. Plus, ``Py_FinalizeEx`` actually
+// calls ``_Py_FinishPendingCalls`` (which sets the pending->finishing
+// flag, which is used to gate creating the exceptioen) *before*
+// publishing any other data that would let us detect the shutdown
+// (such as runtime->finalizing). So that point is moot.
+//
+// Our solution for those versions is to inline the same code, without
+// the problematic bit that sets the exception. Unfortunately, all of
+// the structure definitions are private/opaque, *and* we can't
+// actually count on being able to include their definitions from
+// ``internal/pycore_*``, because on some platforms those header files
+// are incomplete (i.e., on macOS with macports 3.8, the includes are
+// fine, but on Ubuntu jammy with 3.8 from ppa:deadsnakes or GitHub
+// Actions 3.8 (I think it's Ubuntu 18.04), they con't be used; at
+// least, I couldn't get them to work). So we need to define the
+// structures and _PyRuntime data member ourself. Yet more
+// unfortunately, _PyRuntime won't link on Windows, so we can only do
+// this on other platforms.
+//
+// [1] https://github.com/python/cpython/commit/842a2f07f2f08a935ef470bfdaeef40f87490cfc
+// [2] https://github.com/python/cpython/commit/cfc3c2f8b34d3864717ab584c5b6c260014ba55a
+// [3] https://github.com/python/cpython/issues/81308
+# define GREENLET_BROKEN_PY_ADD_PENDING 1
+
+// When defining these structures, the important thing is to get
+// binary compatibility, i.e., structure layout. For that, we only
+// need to define fields up to the ones we use; after that they're
+// irrelevant UNLESS the structure is included in another structure
+// *before* the structure we're interested in --- in that case, it
+// must be complete. Ellipsis indicate elided trailing members.
+// Pointer types are changed to void* to keep from having to define
+// more structures.
+
+// From "internal/pycore_atomic.h"
+
+// There are several different definitions of this, including the
+// plain ``int`` version, a ``volatile int`` and an ``_Atomic int``
+// I don't think any of those change the size/layout.
+typedef struct _Py_atomic_int {
+ volatile int _value;
+} _Py_atomic_int;
+
+// This needs too much infrastructure, so we just do a regular store.
+#define _Py_atomic_store_relaxed(ATOMIC_VAL, NEW_VAL) \
+ (ATOMIC_VAL)->_value = NEW_VAL
+
+
+
+// From "internal/pycore_pymem.h"
+#define NUM_GENERATIONS 3
+
+
+struct gc_generation {
+ PyGC_Head head; // We already have this defined.
+ int threshold;
+ int count;
+};
+struct gc_generation_stats {
+ Py_ssize_t collections;
+ Py_ssize_t collected;
+ Py_ssize_t uncollectable;
+};
+
+struct _gc_runtime_state {
+ void *trash_delete_later;
+ int trash_delete_nesting;
+ int enabled;
+ int debug;
+ struct gc_generation generations[NUM_GENERATIONS];
+ void *generation0;
+ struct gc_generation permanent_generation;
+ struct gc_generation_stats generation_stats[NUM_GENERATIONS];
+ int collecting;
+ void *garbage;
+ void *callbacks;
+ Py_ssize_t long_lived_total;
+ Py_ssize_t long_lived_pending;
+};
+
+// From "internal/pycore_pystate.h"
+struct _pending_calls {
+ int finishing;
+ PyThread_type_lock lock;
+ _Py_atomic_int calls_to_do;
+ int async_exc;
+#define NPENDINGCALLS 32
+ struct {
+ int (*func)(void *);
+ void *arg;
+ } calls[NPENDINGCALLS];
+ int first;
+ int last;
+};
+
+struct _ceval_runtime_state {
+ int recursion_limit;
+ int tracing_possible;
+ _Py_atomic_int eval_breaker;
+ _Py_atomic_int gil_drop_request;
+ struct _pending_calls pending;
+ // ...
+};
+
+typedef struct pyruntimestate {
+ int preinitializing;
+ int preinitialized;
+ int core_initialized;
+ int initialized;
+ void *finalizing;
+
+ struct pyinterpreters {
+ PyThread_type_lock mutex;
+ void *head;
+ void *main;
+ int64_t next_id;
+ } interpreters;
+ // XXX Remove this field once we have a tp_* slot.
+ struct _xidregistry {
+ PyThread_type_lock mutex;
+ void *head;
+ } xidregistry;
+
+ unsigned long main_thread;
+
+#define NEXITFUNCS 32
+ void (*exitfuncs[NEXITFUNCS])(void);
+ int nexitfuncs;
+
+ struct _gc_runtime_state gc;
+ struct _ceval_runtime_state ceval;
+ // ...
+} _PyRuntimeState;
+
+#define SIGNAL_PENDING_CALLS(ceval) \
+ do { \
+ _Py_atomic_store_relaxed(&(ceval)->pending.calls_to_do, 1); \
+ _Py_atomic_store_relaxed(&(ceval)->eval_breaker, 1); \
+ } while (0)
+
+extern _PyRuntimeState _PyRuntime;
+
+#else
+# define GREENLET_BROKEN_PY_ADD_PENDING 0
+#endif
+
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_cpython_compat.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_cpython_compat.hpp
new file mode 100644
index 000000000..cdc1617f6
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_cpython_compat.hpp
@@ -0,0 +1,127 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+#ifndef GREENLET_CPYTHON_COMPAT_H
+#define GREENLET_CPYTHON_COMPAT_H
+
+/**
+ * Helpers for compatibility with multiple versions of CPython.
+ */
+
+#define PY_SSIZE_T_CLEAN
+#include "Python.h"
+
+
+#if PY_VERSION_HEX >= 0x30A00B1
+# define GREENLET_PY310 1
+/*
+Python 3.10 beta 1 changed tstate->use_tracing to a nested cframe member.
+See https://github.com/python/cpython/pull/25276
+We have to save and restore this as well.
+*/
+# define GREENLET_USE_CFRAME 1
+#else
+# define GREENLET_USE_CFRAME 0
+# define GREENLET_PY310 0
+#endif
+
+
+
+#if PY_VERSION_HEX >= 0x30B00A4
+/*
+Greenlet won't compile on anything older than Python 3.11 alpha 4 (see
+https://bugs.python.org/issue46090). Summary of breaking internal changes:
+- Python 3.11 alpha 1 changed how frame objects are represented internally.
+ - https://github.com/python/cpython/pull/30122
+- Python 3.11 alpha 3 changed how recursion limits are stored.
+ - https://github.com/python/cpython/pull/29524
+- Python 3.11 alpha 4 changed how exception state is stored. It also includes a
+ change to help greenlet save and restore the interpreter frame "data stack".
+ - https://github.com/python/cpython/pull/30122
+ - https://github.com/python/cpython/pull/30234
+*/
+# define GREENLET_PY311 1
+#else
+# define GREENLET_PY311 0
+#endif
+
+
+#if PY_VERSION_HEX >= 0x30C0000
+# define GREENLET_PY312 1
+#else
+# define GREENLET_PY312 0
+#endif
+
+#ifndef Py_SET_REFCNT
+/* Py_REFCNT and Py_SIZE macros are converted to functions
+https://bugs.python.org/issue39573 */
+# define Py_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt)
+#endif
+
+#ifndef _Py_DEC_REFTOTAL
+/* _Py_DEC_REFTOTAL macro has been removed from Python 3.9 by:
+ https://github.com/python/cpython/commit/49932fec62c616ec88da52642339d83ae719e924
+
+ The symbol we use to replace it was removed by at least 3.12.
+*/
+# ifdef Py_REF_DEBUG
+# if GREENLET_PY312
+# define _Py_DEC_REFTOTAL
+# else
+# define _Py_DEC_REFTOTAL _Py_RefTotal--
+# endif
+# else
+# define _Py_DEC_REFTOTAL
+# endif
+#endif
+// Define these flags like Cython does if we're on an old version.
+#ifndef Py_TPFLAGS_CHECKTYPES
+ #define Py_TPFLAGS_CHECKTYPES 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_INDEX
+ #define Py_TPFLAGS_HAVE_INDEX 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_NEWBUFFER
+ #define Py_TPFLAGS_HAVE_NEWBUFFER 0
+#endif
+
+#ifndef Py_TPFLAGS_HAVE_VERSION_TAG
+ #define Py_TPFLAGS_HAVE_VERSION_TAG 0
+#endif
+
+#define G_TPFLAGS_DEFAULT Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_VERSION_TAG | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_HAVE_NEWBUFFER | Py_TPFLAGS_HAVE_GC
+
+
+#if PY_VERSION_HEX < 0x03090000
+// The official version only became available in 3.9
+# define PyObject_GC_IsTracked(o) _PyObject_GC_IS_TRACKED(o)
+#endif
+
+
+// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2
+#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION)
+static inline void PyThreadState_EnterTracing(PyThreadState *tstate)
+{
+ tstate->tracing++;
+#if PY_VERSION_HEX >= 0x030A00A1
+ tstate->cframe->use_tracing = 0;
+#else
+ tstate->use_tracing = 0;
+#endif
+}
+#endif
+
+// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2
+#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION)
+static inline void PyThreadState_LeaveTracing(PyThreadState *tstate)
+{
+ tstate->tracing--;
+ int use_tracing = (tstate->c_tracefunc != NULL
+ || tstate->c_profilefunc != NULL);
+#if PY_VERSION_HEX >= 0x030A00A1
+ tstate->cframe->use_tracing = use_tracing;
+#else
+ tstate->use_tracing = use_tracing;
+#endif
+}
+#endif
+
+#endif /* GREENLET_CPYTHON_COMPAT_H */
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_exceptions.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_exceptions.hpp
new file mode 100644
index 000000000..3807018bd
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_exceptions.hpp
@@ -0,0 +1,150 @@
+#ifndef GREENLET_EXCEPTIONS_HPP
+#define GREENLET_EXCEPTIONS_HPP
+
+#define PY_SSIZE_T_CLEAN
+#include
+#include
+#include
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+namespace greenlet {
+
+ class PyErrOccurred : public std::runtime_error
+ {
+ public:
+
+ // CAUTION: In debug builds, may run arbitrary Python code.
+ static const PyErrOccurred
+ from_current()
+ {
+ assert(PyErr_Occurred());
+#ifndef NDEBUG
+ // This is not exception safe, and
+ // not necessarily safe in general (what if it switches?)
+ // But we only do this in debug mode, where we are in
+ // tight control of what exceptions are getting raised and
+ // can prevent those issues.
+
+ // You can't call PyObject_Str with a pending exception.
+ PyObject* typ;
+ PyObject* val;
+ PyObject* tb;
+
+ PyErr_Fetch(&typ, &val, &tb);
+ PyObject* typs = PyObject_Str(typ);
+ PyObject* vals = PyObject_Str(val ? val : typ);
+ const char* typ_msg = PyUnicode_AsUTF8(typs);
+ const char* val_msg = PyUnicode_AsUTF8(vals);
+ PyErr_Restore(typ, val, tb);
+
+ std::string msg(typ_msg);
+ msg += ": ";
+ msg += val_msg;
+ PyErrOccurred ex(msg);
+ Py_XDECREF(typs);
+ Py_XDECREF(vals);
+
+ return ex;
+#else
+ return PyErrOccurred();
+#endif
+ }
+
+ PyErrOccurred() : std::runtime_error("")
+ {
+ assert(PyErr_Occurred());
+ }
+
+ PyErrOccurred(const std::string& msg) : std::runtime_error(msg)
+ {
+ assert(PyErr_Occurred());
+ }
+
+ PyErrOccurred(PyObject* exc_kind, const char* const msg)
+ : std::runtime_error(msg)
+ {
+ PyErr_SetString(exc_kind, msg);
+ }
+
+ PyErrOccurred(PyObject* exc_kind, const std::string msg)
+ : std::runtime_error(msg)
+ {
+ // This copies the c_str, so we don't have any lifetime
+ // issues to worry about.
+ PyErr_SetString(exc_kind, msg.c_str());
+ }
+ };
+
+ class TypeError : public PyErrOccurred
+ {
+ public:
+ TypeError(const char* const what)
+ : PyErrOccurred(PyExc_TypeError, what)
+ {
+ }
+ TypeError(const std::string what)
+ : PyErrOccurred(PyExc_TypeError, what)
+ {
+ }
+ };
+
+ class ValueError : public PyErrOccurred
+ {
+ public:
+ ValueError(const char* const what)
+ : PyErrOccurred(PyExc_ValueError, what)
+ {
+ }
+ };
+
+ class AttributeError : public PyErrOccurred
+ {
+ public:
+ AttributeError(const char* const what)
+ : PyErrOccurred(PyExc_AttributeError, what)
+ {
+ }
+ };
+
+ /**
+ * Calls `Py_FatalError` when constructed, so you can't actually
+ * throw this. It just makes static analysis easier.
+ */
+ class PyFatalError : public std::runtime_error
+ {
+ public:
+ PyFatalError(const char* const msg)
+ : std::runtime_error(msg)
+ {
+ Py_FatalError(msg);
+ }
+ };
+
+ static inline PyObject*
+ Require(PyObject* p, const std::string& msg="")
+ {
+ if (!p) {
+ throw PyErrOccurred(msg);
+ }
+ return p;
+ };
+
+ static inline void
+ Require(const int retval)
+ {
+ if (retval < 0) {
+ throw PyErrOccurred();
+ }
+ };
+
+
+};
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_greenlet.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_greenlet.hpp
new file mode 100644
index 000000000..2c44130dc
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_greenlet.hpp
@@ -0,0 +1,774 @@
+#ifndef GREENLET_GREENLET_HPP
+#define GREENLET_GREENLET_HPP
+/*
+ * Declarations of the core data structures.
+*/
+
+#define PY_SSIZE_T_CLEAN
+#include
+
+#include "greenlet_compiler_compat.hpp"
+#include "greenlet_refs.hpp"
+#include "greenlet_cpython_compat.hpp"
+#include "greenlet_allocator.hpp"
+
+using greenlet::refs::OwnedObject;
+using greenlet::refs::OwnedGreenlet;
+using greenlet::refs::OwnedMainGreenlet;
+using greenlet::refs::BorrowedGreenlet;
+
+#if PY_VERSION_HEX < 0x30B00A6
+# define _PyCFrame CFrame
+# define _PyInterpreterFrame _interpreter_frame
+#endif
+
+#if GREENLET_PY312
+# include "internal/pycore_frame.h"
+#endif
+
+// XXX: TODO: Work to remove all virtual functions
+// for speed of calling and size of objects (no vtable).
+// One pattern is the Curiously Recurring Template
+namespace greenlet
+{
+ class ExceptionState
+ {
+ private:
+ G_NO_COPIES_OF_CLS(ExceptionState);
+
+ // Even though these are borrowed objects, we actually own
+ // them, when they're not null.
+ // XXX: Express that in the API.
+ private:
+ _PyErr_StackItem* exc_info;
+ _PyErr_StackItem exc_state;
+ public:
+ ExceptionState();
+ void operator<<(const PyThreadState *const tstate) noexcept;
+ void operator>>(PyThreadState* tstate) noexcept;
+ void clear() noexcept;
+
+ int tp_traverse(visitproc visit, void* arg) noexcept;
+ void tp_clear() noexcept;
+ };
+
+ template
+ void operator<<(const PyThreadState *const tstate, T& exc);
+
+ class PythonStateContext
+ {
+ protected:
+ greenlet::refs::OwnedContext _context;
+ public:
+ inline const greenlet::refs::OwnedContext& context() const
+ {
+ return this->_context;
+ }
+ inline greenlet::refs::OwnedContext& context()
+ {
+ return this->_context;
+ }
+
+ inline void tp_clear()
+ {
+ this->_context.CLEAR();
+ }
+
+ template
+ inline static PyObject* context(T* tstate)
+ {
+ return tstate->context;
+ }
+
+ template
+ inline static void context(T* tstate, PyObject* new_context)
+ {
+ tstate->context = new_context;
+ tstate->context_ver++;
+ }
+ };
+ class SwitchingArgs;
+ class PythonState : public PythonStateContext
+ {
+ public:
+ typedef greenlet::refs::OwnedReference OwnedFrame;
+ private:
+ G_NO_COPIES_OF_CLS(PythonState);
+ // We own this if we're suspended (although currently we don't
+ // tp_traverse into it; that's a TODO). If we're running, it's
+ // empty. If we get deallocated and *still* have a frame, it
+ // won't be reachable from the place that normally decref's
+ // it, so we need to do it (hence owning it).
+ OwnedFrame _top_frame;
+#if GREENLET_USE_CFRAME
+ _PyCFrame* cframe;
+ int use_tracing;
+#endif
+#if GREENLET_PY312
+ int py_recursion_depth;
+ int c_recursion_depth;
+#else
+ int recursion_depth;
+#endif
+ int trash_delete_nesting;
+#if GREENLET_PY311
+ _PyInterpreterFrame* current_frame;
+ _PyStackChunk* datastack_chunk;
+ PyObject** datastack_top;
+ PyObject** datastack_limit;
+#endif
+#if GREENLET_PY312
+ _PyInterpreterFrame* _prev_frame;
+#endif
+
+ public:
+ PythonState();
+ // You can use this for testing whether we have a frame
+ // or not. It returns const so they can't modify it.
+ const OwnedFrame& top_frame() const noexcept;
+
+ inline void operator<<(const PyThreadState *const tstate) noexcept;
+ inline void operator>>(PyThreadState* tstate) noexcept;
+ void clear() noexcept;
+
+ int tp_traverse(visitproc visit, void* arg, bool visit_top_frame) noexcept;
+ void tp_clear(bool own_top_frame) noexcept;
+ void set_initial_state(const PyThreadState* const tstate) noexcept;
+#if GREENLET_USE_CFRAME
+ void set_new_cframe(_PyCFrame& frame) noexcept;
+#endif
+ inline void may_switch_away() noexcept;
+ inline void will_switch_from(PyThreadState *const origin_tstate) noexcept;
+ void did_finish(PyThreadState* tstate) noexcept;
+ };
+
+ class StackState
+ {
+ // By having only plain C (POD) members, no virtual functions
+ // or bases, we get a trivial assignment operator generated
+ // for us. However, that's not safe since we do manage memory.
+ // So we declare an assignment operator that only works if we
+ // don't have any memory allocated. (We don't use
+ // std::shared_ptr for reference counting just to keep this
+ // object small)
+ private:
+ char* _stack_start;
+ char* stack_stop;
+ char* stack_copy;
+ intptr_t _stack_saved;
+ StackState* stack_prev;
+ inline int copy_stack_to_heap_up_to(const char* const stop) noexcept;
+ inline void free_stack_copy() noexcept;
+
+ public:
+ /**
+ * Creates a started, but inactive, state, using *current*
+ * as the previous.
+ */
+ StackState(void* mark, StackState& current);
+ /**
+ * Creates an inactive, unstarted, state.
+ */
+ StackState();
+ ~StackState();
+ StackState(const StackState& other);
+ StackState& operator=(const StackState& other);
+ inline void copy_heap_to_stack(const StackState& current) noexcept;
+ inline int copy_stack_to_heap(char* const stackref, const StackState& current) noexcept;
+ inline bool started() const noexcept;
+ inline bool main() const noexcept;
+ inline bool active() const noexcept;
+ inline void set_active() noexcept;
+ inline void set_inactive() noexcept;
+ inline intptr_t stack_saved() const noexcept;
+ inline char* stack_start() const noexcept;
+ static inline StackState make_main() noexcept;
+#ifdef GREENLET_USE_STDIO
+ friend std::ostream& operator<<(std::ostream& os, const StackState& s);
+#endif
+ };
+#ifdef GREENLET_USE_STDIO
+ std::ostream& operator<<(std::ostream& os, const StackState& s);
+#endif
+
+ class SwitchingArgs
+ {
+ private:
+ G_NO_ASSIGNMENT_OF_CLS(SwitchingArgs);
+ // If args and kwargs are both false (NULL), this is a *throw*, not a
+ // switch. PyErr_... must have been called already.
+ OwnedObject _args;
+ OwnedObject _kwargs;
+ public:
+
+ SwitchingArgs()
+ {}
+
+ SwitchingArgs(const OwnedObject& args, const OwnedObject& kwargs)
+ : _args(args),
+ _kwargs(kwargs)
+ {}
+
+ SwitchingArgs(const SwitchingArgs& other)
+ : _args(other._args),
+ _kwargs(other._kwargs)
+ {}
+
+ const OwnedObject& args()
+ {
+ return this->_args;
+ }
+
+ const OwnedObject& kwargs()
+ {
+ return this->_kwargs;
+ }
+
+ /**
+ * Moves ownership from the argument to this object.
+ */
+ SwitchingArgs& operator<<=(SwitchingArgs& other)
+ {
+ if (this != &other) {
+ this->_args = other._args;
+ this->_kwargs = other._kwargs;
+ other.CLEAR();
+ }
+ return *this;
+ }
+
+ /**
+ * Acquires ownership of the argument (consumes the reference).
+ */
+ SwitchingArgs& operator<<=(PyObject* args)
+ {
+ this->_args = OwnedObject::consuming(args);
+ this->_kwargs.CLEAR();
+ return *this;
+ }
+
+ /**
+ * Acquires ownership of the argument.
+ *
+ * Sets the args to be the given value; clears the kwargs.
+ */
+ SwitchingArgs& operator<<=(OwnedObject& args)
+ {
+ assert(&args != &this->_args);
+ this->_args = args;
+ this->_kwargs.CLEAR();
+ args.CLEAR();
+
+ return *this;
+ }
+
+ explicit operator bool() const noexcept
+ {
+ return this->_args || this->_kwargs;
+ }
+
+ inline void CLEAR()
+ {
+ this->_args.CLEAR();
+ this->_kwargs.CLEAR();
+ }
+
+ const std::string as_str() const noexcept
+ {
+ return PyUnicode_AsUTF8(
+ OwnedObject::consuming(
+ PyUnicode_FromFormat(
+ "SwitchingArgs(args=%R, kwargs=%R)",
+ this->_args.borrow(),
+ this->_kwargs.borrow()
+ )
+ ).borrow()
+ );
+ }
+ };
+
+ class ThreadState;
+
+ class UserGreenlet;
+ class MainGreenlet;
+
+ class Greenlet
+ {
+ private:
+ G_NO_COPIES_OF_CLS(Greenlet);
+ private:
+ // XXX: Work to remove these.
+ friend class ThreadState;
+ friend class UserGreenlet;
+ friend class MainGreenlet;
+ protected:
+ ExceptionState exception_state;
+ SwitchingArgs switch_args;
+ StackState stack_state;
+ PythonState python_state;
+ Greenlet(PyGreenlet* p, const StackState& initial_state);
+ public:
+ Greenlet(PyGreenlet* p);
+ virtual ~Greenlet();
+
+ const OwnedObject context() const;
+
+ // You MUST call this _very_ early in the switching process to
+ // prepare anything that may need prepared. This might perform
+ // garbage collections or otherwise run arbitrary Python code.
+ //
+ // One specific use of it is for Python 3.11+, preventing
+ // running arbitrary code at unsafe times. See
+ // PythonState::may_switch_away().
+ inline void may_switch_away()
+ {
+ this->python_state.may_switch_away();
+ }
+
+ inline void context(refs::BorrowedObject new_context);
+
+ inline SwitchingArgs& args()
+ {
+ return this->switch_args;
+ }
+
+ virtual const refs::BorrowedMainGreenlet main_greenlet() const = 0;
+
+ inline intptr_t stack_saved() const noexcept
+ {
+ return this->stack_state.stack_saved();
+ }
+
+ // This is used by the macro SLP_SAVE_STATE to compute the
+ // difference in stack sizes. It might be nice to handle the
+ // computation ourself, but the type of the result
+ // varies by platform, so doing it in the macro is the
+ // simplest way.
+ inline const char* stack_start() const noexcept
+ {
+ return this->stack_state.stack_start();
+ }
+
+ virtual OwnedObject throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state);
+ virtual OwnedObject g_switch() = 0;
+ /**
+ * Force the greenlet to appear dead. Used when it's not
+ * possible to throw an exception into a greenlet anymore.
+ *
+ * This losses access to the thread state and the main greenlet.
+ */
+ virtual void murder_in_place();
+
+ /**
+ * Called when somebody notices we were running in a dead
+ * thread to allow cleaning up resources (because we can't
+ * raise GreenletExit into it anymore).
+ * This is very similar to ``murder_in_place()``, except that
+ * it DOES NOT lose the main greenlet or thread state.
+ */
+ inline void deactivate_and_free();
+
+
+ // Called when some thread wants to deallocate a greenlet
+ // object.
+ // The thread may or may not be the same thread the greenlet
+ // was running in.
+ // The thread state will be null if the thread the greenlet
+ // was running in was known to have exited.
+ void deallocing_greenlet_in_thread(const ThreadState* current_state);
+
+ // TODO: Figure out how to make these non-public.
+ inline void slp_restore_state() noexcept;
+ inline int slp_save_state(char *const stackref) noexcept;
+
+ inline bool is_currently_running_in_some_thread() const;
+ virtual bool belongs_to_thread(const ThreadState* state) const;
+
+ inline bool started() const
+ {
+ return this->stack_state.started();
+ }
+ inline bool active() const
+ {
+ return this->stack_state.active();
+ }
+ inline bool main() const
+ {
+ return this->stack_state.main();
+ }
+ virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const = 0;
+
+ virtual const OwnedGreenlet parent() const = 0;
+ virtual void parent(const refs::BorrowedObject new_parent) = 0;
+
+ inline const PythonState::OwnedFrame& top_frame()
+ {
+ return this->python_state.top_frame();
+ }
+
+ virtual const OwnedObject& run() const = 0;
+ virtual void run(const refs::BorrowedObject nrun) = 0;
+
+
+ virtual int tp_traverse(visitproc visit, void* arg);
+ virtual int tp_clear();
+
+
+ // Return the thread state that the greenlet is running in, or
+ // null if the greenlet is not running or the thread is known
+ // to have exited.
+ virtual ThreadState* thread_state() const noexcept = 0;
+
+ // Return true if the greenlet is known to have been running
+ // (active) in a thread that has now exited.
+ virtual bool was_running_in_dead_thread() const noexcept = 0;
+
+ // Return a borrowed greenlet that is the Python object
+ // this object represents.
+ virtual BorrowedGreenlet self() const noexcept = 0;
+
+ // For testing. If this returns true, we should pretend that
+ // slp_switch() failed.
+ virtual bool force_slp_switch_error() const noexcept;
+
+ protected:
+ inline void release_args();
+
+ // The functions that must not be inlined are declared virtual.
+ // We also mark them as protected, not private, so that the
+ // compiler is forced to call them through a function pointer.
+ // (A sufficiently smart compiler could directly call a private
+ // virtual function since it can never be overridden in a
+ // subclass).
+
+ // Also TODO: Switch away from integer error codes and to enums,
+ // or throw exceptions when possible.
+ struct switchstack_result_t
+ {
+ int status;
+ Greenlet* the_new_current_greenlet;
+ OwnedGreenlet origin_greenlet;
+
+ switchstack_result_t()
+ : status(0),
+ the_new_current_greenlet(nullptr)
+ {}
+
+ switchstack_result_t(int err)
+ : status(err),
+ the_new_current_greenlet(nullptr)
+ {}
+
+ switchstack_result_t(int err, Greenlet* state, OwnedGreenlet& origin)
+ : status(err),
+ the_new_current_greenlet(state),
+ origin_greenlet(origin)
+ {
+ }
+
+ switchstack_result_t(int err, Greenlet* state, const BorrowedGreenlet& origin)
+ : status(err),
+ the_new_current_greenlet(state),
+ origin_greenlet(origin)
+ {
+ }
+
+ switchstack_result_t(const switchstack_result_t& other)
+ : status(other.status),
+ the_new_current_greenlet(other.the_new_current_greenlet),
+ origin_greenlet(other.origin_greenlet)
+ {}
+
+ switchstack_result_t& operator=(const switchstack_result_t& other)
+ {
+ this->status = other.status;
+ this->the_new_current_greenlet = other.the_new_current_greenlet;
+ this->origin_greenlet = other.origin_greenlet;
+ return *this;
+ }
+ };
+
+ OwnedObject on_switchstack_or_initialstub_failure(
+ Greenlet* target,
+ const switchstack_result_t& err,
+ const bool target_was_me=false,
+ const bool was_initial_stub=false);
+
+ // Returns the previous greenlet we just switched away from.
+ virtual OwnedGreenlet g_switchstack_success() noexcept;
+
+
+ // Check the preconditions for switching to this greenlet; if they
+ // aren't met, throws PyErrOccurred. Most callers will want to
+ // catch this and clear the arguments
+ inline void check_switch_allowed() const;
+ class GreenletStartedWhileInPython : public std::runtime_error
+ {
+ public:
+ GreenletStartedWhileInPython() : std::runtime_error("")
+ {}
+ };
+
+ protected:
+
+
+ /**
+ Perform a stack switch into this greenlet.
+
+ This temporarily sets the global variable
+ ``switching_thread_state`` to this greenlet; as soon as the
+ call to ``slp_switch`` completes, this is reset to NULL.
+ Consequently, this depends on the GIL.
+
+ TODO: Adopt the stackman model and pass ``slp_switch`` a
+ callback function and context pointer; this eliminates the
+ need for global variables altogether.
+
+ Because the stack switch happens in this function, this
+ function can't use its own stack (local) variables, set
+ before the switch, and then accessed after the switch.
+
+ Further, you con't even access ``g_thread_state_global``
+ before and after the switch from the global variable.
+ Because it is thread local some compilers cache it in a
+ register/on the stack, notably new versions of MSVC; this
+ breaks with strange crashes sometime later, because writing
+ to anything in ``g_thread_state_global`` after the switch
+ is actually writing to random memory. For this reason, we
+ call a non-inlined function to finish the operation. (XXX:
+ The ``/GT`` MSVC compiler argument probably fixes that.)
+
+ It is very important that stack switch is 'atomic', i.e. no
+ calls into other Python code allowed (except very few that
+ are safe), because global variables are very fragile. (This
+ should no longer be the case with thread-local variables.)
+
+ */
+ // Made virtual to facilitate subclassing UserGreenlet for testing.
+ virtual switchstack_result_t g_switchstack(void);
+
+class TracingGuard
+{
+private:
+ PyThreadState* tstate;
+public:
+ TracingGuard()
+ : tstate(PyThreadState_GET())
+ {
+ PyThreadState_EnterTracing(this->tstate);
+ }
+
+ ~TracingGuard()
+ {
+ PyThreadState_LeaveTracing(this->tstate);
+ this->tstate = nullptr;
+ }
+
+ inline void CallTraceFunction(const OwnedObject& tracefunc,
+ const greenlet::refs::ImmortalEventName& event,
+ const BorrowedGreenlet& origin,
+ const BorrowedGreenlet& target)
+ {
+ // TODO: This calls tracefunc(event, (origin, target)). Add a shortcut
+ // function for that that's specialized to avoid the Py_BuildValue
+ // string parsing, or start with just using "ON" format with PyTuple_Pack(2,
+ // origin, target). That seems like what the N format is meant
+ // for.
+ // XXX: Why does event not automatically cast back to a PyObject?
+ // It tries to call the "deleted constructor ImmortalEventName
+ // const" instead.
+ assert(tracefunc);
+ assert(event);
+ assert(origin);
+ assert(target);
+ greenlet::refs::NewReference retval(
+ PyObject_CallFunction(
+ tracefunc.borrow(),
+ "O(OO)",
+ event.borrow(),
+ origin.borrow(),
+ target.borrow()
+ ));
+ if (!retval) {
+ throw PyErrOccurred::from_current();
+ }
+ }
+};
+
+ static void
+ g_calltrace(const OwnedObject& tracefunc,
+ const greenlet::refs::ImmortalEventName& event,
+ const greenlet::refs::BorrowedGreenlet& origin,
+ const BorrowedGreenlet& target);
+ private:
+ OwnedObject g_switch_finish(const switchstack_result_t& err);
+
+ };
+
+ class UserGreenlet : public Greenlet
+ {
+ private:
+ static greenlet::PythonAllocator allocator;
+ BorrowedGreenlet _self;
+ OwnedMainGreenlet _main_greenlet;
+ OwnedObject _run_callable;
+ OwnedGreenlet _parent;
+ public:
+ static void* operator new(size_t UNUSED(count));
+ static void operator delete(void* ptr);
+
+ UserGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent);
+ virtual ~UserGreenlet();
+
+ virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const;
+ virtual bool was_running_in_dead_thread() const noexcept;
+ virtual ThreadState* thread_state() const noexcept;
+ virtual OwnedObject g_switch();
+ virtual const OwnedObject& run() const
+ {
+ if (this->started() || !this->_run_callable) {
+ throw AttributeError("run");
+ }
+ return this->_run_callable;
+ }
+ virtual void run(const refs::BorrowedObject nrun);
+
+ virtual const OwnedGreenlet parent() const;
+ virtual void parent(const refs::BorrowedObject new_parent);
+
+ virtual const refs::BorrowedMainGreenlet main_greenlet() const;
+
+ virtual BorrowedGreenlet self() const noexcept;
+ virtual void murder_in_place();
+ virtual bool belongs_to_thread(const ThreadState* state) const;
+ virtual int tp_traverse(visitproc visit, void* arg);
+ virtual int tp_clear();
+ class ParentIsCurrentGuard
+ {
+ private:
+ OwnedGreenlet oldparent;
+ UserGreenlet* greenlet;
+ G_NO_COPIES_OF_CLS(ParentIsCurrentGuard);
+ public:
+ ParentIsCurrentGuard(UserGreenlet* p, const ThreadState& thread_state);
+ ~ParentIsCurrentGuard();
+ };
+ virtual OwnedObject throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state);
+ protected:
+ virtual switchstack_result_t g_initialstub(void* mark);
+ private:
+ // This function isn't meant to return.
+ // This accepts raw pointers and the ownership of them at the
+ // same time. The caller should use ``inner_bootstrap(origin.relinquish_ownership())``.
+ void inner_bootstrap(PyGreenlet* origin_greenlet, PyObject* run);
+ };
+
+ class BrokenGreenlet : public UserGreenlet
+ {
+ private:
+ static greenlet::PythonAllocator allocator;
+ public:
+ bool _force_switch_error = false;
+ bool _force_slp_switch_error = false;
+
+ static void* operator new(size_t UNUSED(count));
+ static void operator delete(void* ptr);
+ BrokenGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent)
+ : UserGreenlet(p, the_parent)
+ {}
+ virtual ~BrokenGreenlet()
+ {}
+
+ virtual switchstack_result_t g_switchstack(void);
+ virtual bool force_slp_switch_error() const noexcept;
+
+ };
+
+ class MainGreenlet : public Greenlet
+ {
+ private:
+ static greenlet::PythonAllocator allocator;
+ refs::BorrowedMainGreenlet _self;
+ ThreadState* _thread_state;
+ G_NO_COPIES_OF_CLS(MainGreenlet);
+ public:
+ static void* operator new(size_t UNUSED(count));
+ static void operator delete(void* ptr);
+
+ MainGreenlet(refs::BorrowedMainGreenlet::PyType*, ThreadState*);
+ virtual ~MainGreenlet();
+
+
+ virtual const OwnedObject& run() const;
+ virtual void run(const refs::BorrowedObject nrun);
+
+ virtual const OwnedGreenlet parent() const;
+ virtual void parent(const refs::BorrowedObject new_parent);
+
+ virtual const refs::BorrowedMainGreenlet main_greenlet() const;
+
+ virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const;
+ virtual bool was_running_in_dead_thread() const noexcept;
+ virtual ThreadState* thread_state() const noexcept;
+ void thread_state(ThreadState*) noexcept;
+ virtual OwnedObject g_switch();
+ virtual BorrowedGreenlet self() const noexcept;
+ virtual int tp_traverse(visitproc visit, void* arg);
+ };
+
+ // Instantiate one on the stack to save the GC state,
+ // and then disable GC. When it goes out of scope, GC will be
+ // restored to its original state. Sadly, these APIs are only
+ // available on 3.10+; luckily, we only need them on 3.11+.
+#if GREENLET_PY310
+ class GCDisabledGuard
+ {
+ private:
+ int was_enabled = 0;
+ public:
+ GCDisabledGuard()
+ : was_enabled(PyGC_IsEnabled())
+ {
+ PyGC_Disable();
+ }
+
+ ~GCDisabledGuard()
+ {
+ if (this->was_enabled) {
+ PyGC_Enable();
+ }
+ }
+ };
+#endif
+
+ OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept;
+
+ //TODO: Greenlet::g_switch() should call this automatically on its
+ //return value. As it is, the module code is calling it.
+ static inline OwnedObject
+ single_result(const OwnedObject& results)
+ {
+ if (results
+ && PyTuple_Check(results.borrow())
+ && PyTuple_GET_SIZE(results.borrow()) == 1) {
+ PyObject* result = PyTuple_GET_ITEM(results.borrow(), 0);
+ assert(result);
+ return OwnedObject::owning(result);
+ }
+ return results;
+ }
+
+
+ static OwnedObject
+ g_handle_exit(const OwnedObject& greenlet_result);
+
+
+ template
+ void operator<<(const PyThreadState *const lhs, T& rhs)
+ {
+ rhs.operator<<(lhs);
+ }
+
+} // namespace greenlet ;
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_internal.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_internal.hpp
new file mode 100644
index 000000000..c8e38494e
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_internal.hpp
@@ -0,0 +1,106 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+#ifndef GREENLET_INTERNAL_H
+#define GREENLET_INTERNAL_H
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wunused-function"
+# pragma clang diagnostic ignored "-Wmissing-field-initializers"
+# pragma clang diagnostic ignored "-Wunused-variable"
+#endif
+
+/**
+ * Implementation helpers.
+ *
+ * C++ templates and inline functions should go here.
+ */
+#define PY_SSIZE_T_CLEAN
+#include "greenlet_compiler_compat.hpp"
+#include "greenlet_cpython_compat.hpp"
+#include "greenlet_exceptions.hpp"
+#include "greenlet_greenlet.hpp"
+#include "greenlet_allocator.hpp"
+
+#include
+#include
+
+#define GREENLET_MODULE
+struct _greenlet;
+typedef struct _greenlet PyGreenlet;
+namespace greenlet {
+
+ class ThreadState;
+
+};
+
+
+#define implementation_ptr_t greenlet::Greenlet*
+
+
+#include "greenlet.h"
+
+G_FP_TMPL_STATIC inline void
+greenlet::refs::MainGreenletExactChecker(void *p)
+{
+ if (!p) {
+ return;
+ }
+ // We control the class of the main greenlet exactly.
+ if (Py_TYPE(p) != &PyGreenlet_Type) {
+ std::string err("MainGreenlet: Expected exactly a greenlet, not a ");
+ err += Py_TYPE(p)->tp_name;
+ throw greenlet::TypeError(err);
+ }
+
+ // Greenlets from dead threads no longer respond to main() with a
+ // true value; so in that case we need to perform an additional
+ // check.
+ Greenlet* g = ((PyGreenlet*)p)->pimpl;
+ if (g->main()) {
+ return;
+ }
+ if (!dynamic_cast(g)) {
+ std::string err("MainGreenlet: Expected exactly a main greenlet, not a ");
+ err += Py_TYPE(p)->tp_name;
+ throw greenlet::TypeError(err);
+ }
+}
+
+
+
+template
+inline greenlet::Greenlet* greenlet::refs::_OwnedGreenlet::operator->() const noexcept
+{
+ return reinterpret_cast(this->p)->pimpl;
+}
+
+template
+inline greenlet::Greenlet* greenlet::refs::_BorrowedGreenlet::operator->() const noexcept
+{
+ return reinterpret_cast(this->p)->pimpl;
+}
+
+#include
+#include
+
+
+extern PyTypeObject PyGreenlet_Type;
+
+
+
+/**
+ * Forward declarations needed in multiple files.
+ */
+static PyGreenlet* green_create_main(greenlet::ThreadState*);
+static PyObject* green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs);
+static int green_is_gc(BorrowedGreenlet self);
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+
+#endif
+
+// Local Variables:
+// flycheck-clang-include-path: ("../../include" "/opt/local/Library/Frameworks/Python.framework/Versions/3.10/include/python3.10")
+// End:
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_refs.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_refs.hpp
new file mode 100644
index 000000000..72ee68b44
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_refs.hpp
@@ -0,0 +1,1100 @@
+#ifndef GREENLET_REFS_HPP
+#define GREENLET_REFS_HPP
+
+#define PY_SSIZE_T_CLEAN
+#include
+//#include "greenlet_internal.hpp"
+#include "greenlet_compiler_compat.hpp"
+#include "greenlet_cpython_compat.hpp"
+#include "greenlet_exceptions.hpp"
+
+struct _greenlet;
+struct _PyMainGreenlet;
+
+typedef struct _greenlet PyGreenlet;
+extern PyTypeObject PyGreenlet_Type;
+
+
+#ifdef GREENLET_USE_STDIO
+#include
+using std::cerr;
+using std::endl;
+#endif
+
+namespace greenlet
+{
+ class Greenlet;
+
+ namespace refs
+ {
+ // Type checkers throw a TypeError if the argument is not
+ // null, and isn't of the required Python type.
+ // (We can't use most of the defined type checkers
+ // like PyList_Check, etc, directly, because they are
+ // implemented as macros.)
+ typedef void (*TypeChecker)(void*);
+
+ G_FP_TMPL_STATIC inline void
+ NoOpChecker(void*)
+ {
+ return;
+ }
+
+ G_FP_TMPL_STATIC inline void
+ GreenletChecker(void *p)
+ {
+ if (!p) {
+ return;
+ }
+
+ PyTypeObject* typ = Py_TYPE(p);
+ // fast, common path. (PyObject_TypeCheck is a macro or
+ // static inline function, and it also does a
+ // direct comparison of the type pointers, but its fast
+ // path only handles one type)
+ if (typ == &PyGreenlet_Type) {
+ return;
+ }
+
+ if (!PyObject_TypeCheck(p, &PyGreenlet_Type)) {
+ std::string err("GreenletChecker: Expected any type of greenlet, not ");
+ err += Py_TYPE(p)->tp_name;
+ throw TypeError(err);
+ }
+ }
+
+ G_FP_TMPL_STATIC inline void
+ MainGreenletExactChecker(void *p);
+
+ template
+ class PyObjectPointer;
+
+ template
+ class OwnedReference;
+
+
+ template
+ class BorrowedReference;
+
+ typedef BorrowedReference BorrowedObject;
+ typedef OwnedReference OwnedObject;
+
+ class ImmortalObject;
+ class ImmortalString;
+
+ template
+ class _OwnedGreenlet;
+
+ typedef _OwnedGreenlet OwnedGreenlet;
+ typedef _OwnedGreenlet OwnedMainGreenlet;
+
+ template
+ class _BorrowedGreenlet;
+
+ typedef _BorrowedGreenlet BorrowedGreenlet;
+
+ G_FP_TMPL_STATIC inline void
+ ContextExactChecker(void *p)
+ {
+ if (!p) {
+ return;
+ }
+ if (!PyContext_CheckExact(p)) {
+ throw TypeError(
+ "greenlet context must be a contextvars.Context or None"
+ );
+ }
+ }
+
+ typedef OwnedReference OwnedContext;
+ }
+}
+
+namespace greenlet {
+
+
+ namespace refs {
+ // A set of classes to make reference counting rules in python
+ // code explicit.
+ //
+ // Rules of use:
+ // (1) Functions returning a new reference that the caller of the
+ // function is expected to dispose of should return a
+ // ``OwnedObject`` object. This object automatically releases its
+ // reference when it goes out of scope. It works like a ``std::shared_ptr``
+ // and can be copied or used as a function parameter (but don't do
+ // that). Note that constructing a ``OwnedObject`` from a
+ // PyObject* steals the reference.
+ // (2) Parameters to functions should be either a
+ // ``OwnedObject&``, or, more generally, a ``PyObjectPointer&``.
+ // If the function needs to create its own new reference, it can
+ // do so by copying to a local ``OwnedObject``.
+ // (3) Functions returning an existing pointer that is NOT
+ // incref'd, and which the caller MUST NOT decref,
+ // should return a ``BorrowedObject``.
+
+ //
+ // For a class with a single pointer member, whose constructor
+ // does nothing but copy a pointer parameter into the member, and
+ // which can then be converted back to the pointer type, compilers
+ // generate code that's the same as just passing the pointer.
+ // That is, func(BorrowedObject x) called like ``PyObject* p =
+ // ...; f(p)`` has 0 overhead. Similarly, they "unpack" to the
+ // pointer type with 0 overhead.
+ //
+ // If there are no virtual functions, no complex inheritance (maybe?) and
+ // no destructor, these can be directly used as parameters in
+ // Python callbacks like tp_init: the layout is the same as a
+ // single pointer. Only subclasses with trivial constructors that
+ // do nothing but set the single pointer member are safe to use
+ // that way.
+
+
+ // This is the base class for things that can be done with a
+ // PyObject pointer. It assumes nothing about memory management.
+ // NOTE: Nothing is virtual, so subclasses shouldn't add new
+ // storage fields or try to override these methods.
+ template
+ class PyObjectPointer
+ {
+ public:
+ typedef T PyType;
+ protected:
+ T* p;
+ public:
+ explicit PyObjectPointer(T* it=nullptr) : p(it)
+ {
+ TC(p);
+ }
+
+ // We don't allow automatic casting to PyObject* at this
+ // level, because then we could be passed to Py_DECREF/INCREF,
+ // but we want nothing to do with memory management. If you
+ // know better, then you can use the get() method, like on a
+ // std::shared_ptr. Except we name it borrow() to clarify that
+ // if this is a reference-tracked object, the pointer you get
+ // back will go away when the object does.
+ // TODO: This should probably not exist here, but be moved
+ // down to relevant sub-types.
+
+ inline T* borrow() const noexcept
+ {
+ return this->p;
+ }
+
+ PyObject* borrow_o() const noexcept
+ {
+ return reinterpret_cast(this->p);
+ }
+
+ inline T* operator->() const noexcept
+ {
+ return this->p;
+ }
+
+ bool is_None() const noexcept
+ {
+ return this->p == Py_None;
+ }
+
+ inline PyObject* acquire_or_None() const noexcept
+ {
+ PyObject* result = this->p ? reinterpret_cast(this->p) : Py_None;
+ Py_INCREF(result);
+ return result;
+ }
+
+ explicit operator bool() const noexcept
+ {
+ return p != nullptr;
+ }
+
+ inline Py_ssize_t REFCNT() const noexcept
+ {
+ return p ? Py_REFCNT(p) : -42;
+ }
+
+ inline PyTypeObject* TYPE() const noexcept
+ {
+ return p ? Py_TYPE(p) : nullptr;
+ }
+
+ inline OwnedObject PyStr() const noexcept;
+ inline const std::string as_str() const noexcept;
+ inline OwnedObject PyGetAttr(const ImmortalObject& name) const noexcept;
+ inline OwnedObject PyRequireAttr(const char* const name) const;
+ inline OwnedObject PyRequireAttr(const ImmortalString& name) const;
+ inline OwnedObject PyCall(const BorrowedObject& arg) const;
+ inline OwnedObject PyCall(PyGreenlet* arg) const ;
+ inline OwnedObject PyCall(PyObject* arg) const ;
+ // PyObject_Call(this, args, kwargs);
+ inline OwnedObject PyCall(const BorrowedObject args,
+ const BorrowedObject kwargs) const;
+ inline OwnedObject PyCall(const OwnedObject& args,
+ const OwnedObject& kwargs) const;
+
+ protected:
+ void _set_raw_pointer(void* t)
+ {
+ TC(t);
+ p = reinterpret_cast(t);
+ }
+ void* _get_raw_pointer() const
+ {
+ return p;
+ }
+ };
+
+#ifdef GREENLET_USE_STDIO
+ template
+ std::ostream& operator<<(std::ostream& os, const PyObjectPointer& s)
+ {
+ const std::type_info& t = typeid(s);
+ os << t.name()
+ << "(addr=" << s.borrow()
+ << ", refcnt=" << s.REFCNT()
+ << ", value=" << s.as_str()
+ << ")";
+
+ return os;
+ }
+#endif
+
+ template
+ inline bool operator==(const PyObjectPointer& lhs, const void* const rhs) noexcept
+ {
+ return lhs.borrow_o() == rhs;
+ }
+
+ template
+ inline bool operator==(const PyObjectPointer& lhs, const PyObjectPointer& rhs) noexcept
+ {
+ return lhs.borrow_o() == rhs.borrow_o();
+ }
+
+ template
+ inline bool operator!=(const PyObjectPointer& lhs,
+ const PyObjectPointer& rhs) noexcept
+ {
+ return lhs.borrow_o() != rhs.borrow_o();
+ }
+
+ template
+ class OwnedReference : public PyObjectPointer
+ {
+ private:
+ friend class OwnedList;
+
+ protected:
+ explicit OwnedReference(T* it) : PyObjectPointer(it)
+ {
+ }
+
+ public:
+
+ // Constructors
+
+ static OwnedReference consuming(PyObject* p)
+ {
+ return OwnedReference(reinterpret_cast(p));
+ }
+
+ static OwnedReference owning(T* p)
+ {
+ OwnedReference result(p);
+ Py_XINCREF(result.p);
+ return result;
+ }
+
+ OwnedReference() : PyObjectPointer(nullptr)
+ {}
+
+ explicit OwnedReference(const PyObjectPointer<>& other)
+ : PyObjectPointer(nullptr)
+ {
+ T* op = other.borrow();
+ TC(op);
+ this->p = other.borrow();
+ Py_XINCREF(this->p);
+ }
+
+ // It would be good to make use of the C++11 distinction
+ // between move and copy operations, e.g., constructing from a
+ // pointer should be a move operation.
+ // In the common case of ``OwnedObject x = Py_SomeFunction()``,
+ // the call to the copy constructor will be elided completely.
+ OwnedReference(const OwnedReference& other)
+ : PyObjectPointer(other.p)
+ {
+ Py_XINCREF(this->p);
+ }
+
+ static OwnedReference None()
+ {
+ Py_INCREF(Py_None);
+ return OwnedReference(Py_None);
+ }
+
+ // We can assign from exactly our type without any extra checking
+ OwnedReference& operator=(const OwnedReference& other)
+ {
+ Py_XINCREF(other.p);
+ const T* tmp = this->p;
+ this->p = other.p;
+ Py_XDECREF(tmp);
+ return *this;
+ }
+
+ OwnedReference& operator=(const BorrowedReference other)
+ {
+ return this->operator=(other.borrow());
+ }
+
+ OwnedReference& operator=(T* const other)
+ {
+ TC(other);
+ Py_XINCREF(other);
+ T* tmp = this->p;
+ this->p = other;
+ Py_XDECREF(tmp);
+ return *this;
+ }
+
+ // We can assign from an arbitrary reference type
+ // if it passes our check.
+ template
+ OwnedReference& operator=(const OwnedReference& other)
+ {
+ X* op = other.borrow();
+ TC(op);
+ return this->operator=(reinterpret_cast(op));
+ }
+
+ inline void steal(T* other)
+ {
+ assert(this->p == nullptr);
+ TC(other);
+ this->p = other;
+ }
+
+ T* relinquish_ownership()
+ {
+ T* result = this->p;
+ this->p = nullptr;
+ return result;
+ }
+
+ T* acquire() const
+ {
+ // Return a new reference.
+ // TODO: This may go away when we have reference objects
+ // throughout the code.
+ Py_XINCREF(this->p);
+ return this->p;
+ }
+
+ // Nothing else declares a destructor, we're the leaf, so we
+ // should be able to get away without virtual.
+ ~OwnedReference()
+ {
+ Py_CLEAR(this->p);
+ }
+
+ void CLEAR()
+ {
+ Py_CLEAR(this->p);
+ assert(this->p == nullptr);
+ }
+ };
+
+ static inline
+ void operator<<=(PyObject*& target, OwnedObject& o)
+ {
+ target = o.relinquish_ownership();
+ }
+
+ class NewReference : public OwnedObject
+ {
+ private:
+ G_NO_COPIES_OF_CLS(NewReference);
+ public:
+ // Consumes the reference. Only use this
+ // for API return values.
+ NewReference(PyObject* it) : OwnedObject(it)
+ {
+ }
+ };
+
+ class NewDictReference : public NewReference
+ {
+ private:
+ G_NO_COPIES_OF_CLS(NewDictReference);
+ public:
+ NewDictReference() : NewReference(PyDict_New())
+ {
+ if (!this->p) {
+ throw PyErrOccurred();
+ }
+ }
+
+ void SetItem(const char* const key, PyObject* value)
+ {
+ Require(PyDict_SetItemString(this->p, key, value));
+ }
+
+ void SetItem(const PyObjectPointer<>& key, PyObject* value)
+ {
+ Require(PyDict_SetItem(this->p, key.borrow_o(), value));
+ }
+ };
+
+ template
+ class _OwnedGreenlet: public OwnedReference
+ {
+ private:
+ protected:
+ _OwnedGreenlet(T* it) : OwnedReference(it)
+ {}
+
+ public:
+ _OwnedGreenlet() : OwnedReference()
+ {}
+
+ _OwnedGreenlet(const _OwnedGreenlet& other) : OwnedReference(other)
+ {
+ }
+ _OwnedGreenlet(OwnedMainGreenlet& other) :
+ OwnedReference(reinterpret_cast(other.acquire()))
+ {
+ }
+ _OwnedGreenlet(const BorrowedGreenlet& other);
+ // Steals a reference.
+ static _OwnedGreenlet consuming(PyGreenlet* it)
+ {
+ return _OwnedGreenlet(reinterpret_cast(it));
+ }
+
+ inline _OwnedGreenlet& operator=(const OwnedGreenlet& other)
+ {
+ return this->operator=(other.borrow());
+ }
+
+ inline _OwnedGreenlet& operator=(const BorrowedGreenlet& other);
+
+ _OwnedGreenlet& operator=(const OwnedMainGreenlet& other)
+ {
+ PyGreenlet* owned = other.acquire();
+ Py_XDECREF(this->p);
+ this->p = reinterpret_cast(owned);
+ return *this;
+ }
+
+ _OwnedGreenlet& operator=(T* const other)
+ {
+ OwnedReference::operator=(other);
+ return *this;
+ }
+
+ T* relinquish_ownership()
+ {
+ T* result = this->p;
+ this->p = nullptr;
+ return result;
+ }
+
+ PyObject* relinquish_ownership_o()
+ {
+ return reinterpret_cast(relinquish_ownership());
+ }
+
+ inline Greenlet* operator->() const noexcept;
+ inline operator Greenlet*() const noexcept;
+ };
+
+ template
+ class BorrowedReference : public PyObjectPointer
+ {
+ public:
+ // Allow implicit creation from PyObject* pointers as we
+ // transition to using these classes. Also allow automatic
+ // conversion to PyObject* for passing to C API calls and even
+ // for Py_INCREF/DECREF, because we ourselves do no memory management.
+ BorrowedReference(T* it) : PyObjectPointer(it)
+ {}
+
+ BorrowedReference(const PyObjectPointer& ref) : PyObjectPointer(ref.borrow())
+ {}
+
+ BorrowedReference() : PyObjectPointer(nullptr)
+ {}
+
+ operator T*() const
+ {
+ return this->p;
+ }
+ };
+
+ typedef BorrowedReference BorrowedObject;
+ //typedef BorrowedReference BorrowedGreenlet;
+
+ template
+ class _BorrowedGreenlet : public BorrowedReference
+ {
+ public:
+ _BorrowedGreenlet() :
+ BorrowedReference(nullptr)
+ {}
+
+ _BorrowedGreenlet(T* it) :
+ BorrowedReference(it)
+ {}
+
+ _BorrowedGreenlet(const BorrowedObject& it);
+
+ _BorrowedGreenlet(const OwnedGreenlet& it) :
+ BorrowedReference(it.borrow())
+ {}
+
+ _BorrowedGreenlet& operator=(const BorrowedObject& other);
+
+ // We get one of these for PyGreenlet, but one for PyObject
+ // is handy as well
+ operator PyObject*() const
+ {
+ return reinterpret_cast(this->p);
+ }
+ inline Greenlet* operator->() const noexcept;
+ inline operator Greenlet*() const noexcept;
+ };
+
+ typedef _BorrowedGreenlet BorrowedGreenlet;
+
+ template
+ _OwnedGreenlet::_OwnedGreenlet(const BorrowedGreenlet& other)
+ : OwnedReference(reinterpret_cast(other.borrow()))
+ {
+ Py_XINCREF(this->p);
+ }
+
+
+ class BorrowedMainGreenlet
+ : public _BorrowedGreenlet
+ {
+ public:
+ BorrowedMainGreenlet(const OwnedMainGreenlet& it) :
+ _BorrowedGreenlet(it.borrow())
+ {}
+ BorrowedMainGreenlet(PyGreenlet* it=nullptr)
+ : _BorrowedGreenlet(it)
+ {}
+ };
+
+ template
+ _OwnedGreenlet& _OwnedGreenlet::operator=(const BorrowedGreenlet& other)
+ {
+ return this->operator=(other.borrow());
+ }
+
+
+ class ImmortalObject : public PyObjectPointer<>
+ {
+ private:
+ G_NO_ASSIGNMENT_OF_CLS(ImmortalObject);
+ public:
+ explicit ImmortalObject(PyObject* it) : PyObjectPointer<>(it)
+ {
+ }
+
+ ImmortalObject(const ImmortalObject& other)
+ : PyObjectPointer<>(other.p)
+ {
+
+ }
+
+ /**
+ * Become the new owner of the object. Does not change the
+ * reference count.
+ */
+ ImmortalObject& operator=(PyObject* it)
+ {
+ assert(this->p == nullptr);
+ this->p = it;
+ return *this;
+ }
+
+ static ImmortalObject consuming(PyObject* it)
+ {
+ return ImmortalObject(it);
+ }
+
+ inline operator PyObject*() const
+ {
+ return this->p;
+ }
+ };
+
+ class ImmortalString : public ImmortalObject
+ {
+ private:
+ G_NO_COPIES_OF_CLS(ImmortalString);
+ const char* str;
+ public:
+ ImmortalString(const char* const str) :
+ ImmortalObject(str ? Require(PyUnicode_InternFromString(str)) : nullptr)
+ {
+ this->str = str;
+ }
+
+ inline ImmortalString& operator=(const char* const str)
+ {
+ if (!this->p) {
+ this->p = Require(PyUnicode_InternFromString(str));
+ this->str = str;
+ }
+ else {
+ assert(this->str == str);
+ }
+ return *this;
+ }
+
+ inline operator std::string() const
+ {
+ return this->str;
+ }
+
+ };
+
+ class ImmortalEventName : public ImmortalString
+ {
+ private:
+ G_NO_COPIES_OF_CLS(ImmortalEventName);
+ public:
+ ImmortalEventName(const char* const str) : ImmortalString(str)
+ {}
+ };
+
+ class ImmortalException : public ImmortalObject
+ {
+ private:
+ G_NO_COPIES_OF_CLS(ImmortalException);
+ public:
+ ImmortalException(const char* const name, PyObject* base=nullptr) :
+ ImmortalObject(name
+ // Python 2.7 isn't const correct
+ ? Require(PyErr_NewException((char*)name, base, nullptr))
+ : nullptr)
+ {}
+
+ inline bool PyExceptionMatches() const
+ {
+ return PyErr_ExceptionMatches(this->p) > 0;
+ }
+
+ };
+
+ template
+ inline OwnedObject PyObjectPointer::PyStr() const noexcept
+ {
+ if (!this->p) {
+ return OwnedObject();
+ }
+ return OwnedObject::consuming(PyObject_Str(reinterpret_cast(this->p)));
+ }
+
+ template
+ inline const std::string PyObjectPointer::as_str() const noexcept
+ {
+ // NOTE: This is not Python exception safe.
+ if (this->p) {
+ // The Python APIs return a cached char* value that's only valid
+ // as long as the original object stays around, and we're
+ // about to (probably) toss it. Hence the copy to std::string.
+ OwnedObject py_str = this->PyStr();
+ if (!py_str) {
+ return "(nil)";
+ }
+ return PyUnicode_AsUTF8(py_str.borrow());
+ }
+ return "(nil)";
+ }
+
+ template
+ inline OwnedObject PyObjectPointer::PyGetAttr(const ImmortalObject& name) const noexcept
+ {
+ assert(this->p);
+ return OwnedObject::consuming(PyObject_GetAttr(reinterpret_cast(this->p), name));
+ }
+
+ template
+ inline OwnedObject PyObjectPointer::PyRequireAttr(const char* const name) const
+ {
+ assert(this->p);
+ return OwnedObject::consuming(Require(PyObject_GetAttrString(this->p, name), name));
+ }
+
+ template
+ inline OwnedObject PyObjectPointer::PyRequireAttr(const ImmortalString& name) const
+ {
+ assert(this->p);
+ return OwnedObject::consuming(Require(
+ PyObject_GetAttr(
+ reinterpret_cast(this->p),
+ name
+ ),
+ name
+ ));
+ }
+
+ template
+ inline OwnedObject PyObjectPointer::PyCall(const BorrowedObject& arg) const
+ {
+ return this->PyCall(arg.borrow());
+ }
+
+ template
+ inline OwnedObject PyObjectPointer::PyCall(PyGreenlet* arg) const
+ {
+ return this->PyCall(reinterpret_cast(arg));
+ }
+
+ template
+ inline OwnedObject PyObjectPointer::PyCall(PyObject* arg) const
+ {
+ assert(this->p);
+ return OwnedObject::consuming(PyObject_CallFunctionObjArgs(this->p, arg, NULL));
+ }
+
+ template
+ inline OwnedObject PyObjectPointer::PyCall(const BorrowedObject args,
+ const BorrowedObject kwargs) const
+ {
+ assert(this->p);
+ return OwnedObject::consuming(PyObject_Call(this->p, args, kwargs));
+ }
+
+ template
+ inline OwnedObject PyObjectPointer::PyCall(const OwnedObject& args,
+ const OwnedObject& kwargs) const
+ {
+ assert(this->p);
+ return OwnedObject::consuming(PyObject_Call(this->p, args.borrow(), kwargs.borrow()));
+ }
+
+ G_FP_TMPL_STATIC inline void
+ ListChecker(void * p)
+ {
+ if (!p) {
+ return;
+ }
+ if (!PyList_Check(p)) {
+ throw TypeError("Expected a list");
+ }
+ }
+
+ class OwnedList : public OwnedReference
+ {
+ private:
+ G_NO_ASSIGNMENT_OF_CLS(OwnedList);
+ public:
+ // TODO: Would like to use move.
+ explicit OwnedList(const OwnedObject& other)
+ : OwnedReference(other)
+ {
+ }
+
+ OwnedList& operator=(const OwnedObject& other)
+ {
+ if (other && PyList_Check(other.p)) {
+ // Valid list. Own a new reference to it, discard the
+ // reference to what we did own.
+ PyObject* new_ptr = other.p;
+ Py_INCREF(new_ptr);
+ Py_XDECREF(this->p);
+ this->p = new_ptr;
+ }
+ else {
+ // Either the other object was NULL (an error) or it
+ // wasn't a list. Either way, we're now invalidated.
+ Py_XDECREF(this->p);
+ this->p = nullptr;
+ }
+ return *this;
+ }
+
+ inline bool empty() const
+ {
+ return PyList_GET_SIZE(p) == 0;
+ }
+
+ inline Py_ssize_t size() const
+ {
+ return PyList_GET_SIZE(p);
+ }
+
+ inline BorrowedObject at(const Py_ssize_t index) const
+ {
+ return PyList_GET_ITEM(p, index);
+ }
+
+ inline void clear()
+ {
+ PyList_SetSlice(p, 0, PyList_GET_SIZE(p), NULL);
+ }
+ };
+
+ // Use this to represent the module object used at module init
+ // time.
+ // This could either be a borrowed (Py2) or new (Py3) reference;
+ // either way, we don't want to do any memory management
+ // on it here, Python itself will handle that.
+ // XXX: Actually, that's not quite right. On Python 3, if an
+ // exception occurs before we return to the interpreter, this will
+ // leak; but all previous versions also had that problem.
+ class CreatedModule : public PyObjectPointer<>
+ {
+ private:
+ G_NO_COPIES_OF_CLS(CreatedModule);
+ public:
+ CreatedModule(PyModuleDef& mod_def) : PyObjectPointer<>(
+ Require(PyModule_Create(&mod_def)))
+ {
+ }
+
+ // PyAddObject(): Add a reference to the object to the module.
+ // On return, the reference count of the object is unchanged.
+ //
+ // The docs warn that PyModule_AddObject only steals the
+ // reference on success, so if it fails after we've incref'd
+ // or allocated, we're responsible for the decref.
+ void PyAddObject(const char* name, const long new_bool)
+ {
+ OwnedObject p = OwnedObject::consuming(Require(PyBool_FromLong(new_bool)));
+ this->PyAddObject(name, p);
+ }
+
+ void PyAddObject(const char* name, const OwnedObject& new_object)
+ {
+ // The caller already owns a reference they will decref
+ // when their variable goes out of scope, we still need to
+ // incref/decref.
+ this->PyAddObject(name, new_object.borrow());
+ }
+
+ void PyAddObject(const char* name, const ImmortalObject& new_object)
+ {
+ this->PyAddObject(name, new_object.borrow());
+ }
+
+ void PyAddObject(const char* name, PyTypeObject& type)
+ {
+ this->PyAddObject(name, reinterpret_cast(&type));
+ }
+
+ void PyAddObject(const char* name, PyObject* new_object)
+ {
+ Py_INCREF(new_object);
+ try {
+ Require(PyModule_AddObject(this->p, name, new_object));
+ }
+ catch (const PyErrOccurred&) {
+ Py_DECREF(p);
+ throw;
+ }
+ }
+ };
+
+ class PyErrFetchParam : public PyObjectPointer<>
+ {
+ // Not an owned object, because we can't be initialized with
+ // one, and we only sometimes acquire ownership.
+ private:
+ G_NO_COPIES_OF_CLS(PyErrFetchParam);
+ public:
+ // To allow declaring these and passing them to
+ // PyErr_Fetch we implement the empty constructor,
+ // and the address operator.
+ PyErrFetchParam() : PyObjectPointer<>(nullptr)
+ {
+ }
+
+ PyObject** operator&()
+ {
+ return &this->p;
+ }
+
+ // This allows us to pass one directly without the &,
+ // BUT it has higher precedence than the bool operator
+ // if it's not explicit.
+ operator PyObject**()
+ {
+ return &this->p;
+ }
+
+ // We don't want to be able to pass these to Py_DECREF and
+ // such so we don't have the implicit PyObject* conversion.
+
+ inline PyObject* relinquish_ownership()
+ {
+ PyObject* result = this->p;
+ this->p = nullptr;
+ return result;
+ }
+
+ ~PyErrFetchParam()
+ {
+ Py_XDECREF(p);
+ }
+ };
+
+ class OwnedErrPiece : public OwnedObject
+ {
+ private:
+
+ public:
+ // Unlike OwnedObject, this increments the refcount.
+ OwnedErrPiece(PyObject* p=nullptr) : OwnedObject(p)
+ {
+ this->acquire();
+ }
+
+ PyObject** operator&()
+ {
+ return &this->p;
+ }
+
+ inline operator PyObject*() const
+ {
+ return this->p;
+ }
+
+ operator PyTypeObject*() const
+ {
+ return reinterpret_cast(this->p);
+ }
+ };
+
+ class PyErrPieces
+ {
+ private:
+ OwnedErrPiece type;
+ OwnedErrPiece instance;
+ OwnedErrPiece traceback;
+ bool restored;
+ public:
+ // Takes new references; if we're destroyed before
+ // restoring the error, we drop the references.
+ PyErrPieces(PyObject* t, PyObject* v, PyObject* tb) :
+ type(t),
+ instance(v),
+ traceback(tb),
+ restored(0)
+ {
+ this->normalize();
+ }
+
+ PyErrPieces() :
+ restored(0)
+ {
+ // PyErr_Fetch transfers ownership to us, so
+ // we don't actually need to INCREF; but we *do*
+ // need to DECREF if we're not restored.
+ PyErrFetchParam t, v, tb;
+ PyErr_Fetch(&t, &v, &tb);
+ type.steal(t.relinquish_ownership());
+ instance.steal(v.relinquish_ownership());
+ traceback.steal(tb.relinquish_ownership());
+ }
+
+ void PyErrRestore()
+ {
+ // can only do this once
+ assert(!this->restored);
+ this->restored = true;
+ PyErr_Restore(
+ this->type.relinquish_ownership(),
+ this->instance.relinquish_ownership(),
+ this->traceback.relinquish_ownership());
+ assert(!this->type && !this->instance && !this->traceback);
+ }
+
+ private:
+ void normalize()
+ {
+ // First, check the traceback argument, replacing None,
+ // with NULL
+ if (traceback.is_None()) {
+ traceback = nullptr;
+ }
+
+ if (traceback && !PyTraceBack_Check(traceback.borrow())) {
+ throw PyErrOccurred(PyExc_TypeError,
+ "throw() third argument must be a traceback object");
+ }
+
+ if (PyExceptionClass_Check(type)) {
+ // If we just had a type, we'll now have a type and
+ // instance.
+ // The type's refcount will have gone up by one
+ // because of the instance and the instance will have
+ // a refcount of one. Either way, we owned, and still
+ // do own, exactly one reference.
+ PyErr_NormalizeException(&type, &instance, &traceback);
+
+ }
+ else if (PyExceptionInstance_Check(type)) {
+ /* Raising an instance --- usually that means an
+ object that is a subclass of BaseException, but on
+ Python 2, that can also mean an arbitrary old-style
+ object. The value should be a dummy. */
+ if (instance && !instance.is_None()) {
+ throw PyErrOccurred(
+ PyExc_TypeError,
+ "instance exception may not have a separate value");
+ }
+ /* Normalize to raise , */
+ this->instance = this->type;
+ this->type = PyExceptionInstance_Class(instance.borrow());
+
+ /*
+ It would be tempting to do this:
+
+ Py_ssize_t type_count = Py_REFCNT(Py_TYPE(instance.borrow()));
+ this->type = PyExceptionInstance_Class(instance.borrow());
+ assert(this->type.REFCNT() == type_count + 1);
+
+ But that doesn't work on Python 2 in the case of
+ old-style instances: The result of Py_TYPE is going to
+ be the global shared that all
+ old-style classes have, while the return of Instance_Class()
+ will be the Python-level class object. The two are unrelated.
+ */
+ }
+ else {
+ /* Not something you can raise. throw() fails. */
+ PyErr_Format(PyExc_TypeError,
+ "exceptions must be classes, or instances, not %s",
+ Py_TYPE(type.borrow())->tp_name);
+ throw PyErrOccurred();
+ }
+ }
+ };
+
+ // PyArg_Parse's O argument returns a borrowed reference.
+ class PyArgParseParam : public BorrowedObject
+ {
+ private:
+ G_NO_COPIES_OF_CLS(PyArgParseParam);
+ public:
+ explicit PyArgParseParam(PyObject* p=nullptr) : BorrowedObject(p)
+ {
+ }
+
+ inline PyObject** operator&()
+ {
+ return &this->p;
+ }
+ };
+
+};};
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_slp_switch.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_slp_switch.hpp
new file mode 100644
index 000000000..bd4b7ae1a
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_slp_switch.hpp
@@ -0,0 +1,99 @@
+#ifndef GREENLET_SLP_SWITCH_HPP
+#define GREENLET_SLP_SWITCH_HPP
+
+#include "greenlet_compiler_compat.hpp"
+#include "greenlet_refs.hpp"
+
+/*
+ * the following macros are spliced into the OS/compiler
+ * specific code, in order to simplify maintenance.
+ */
+// We can save about 10% of the time it takes to switch greenlets if
+// we thread the thread state through the slp_save_state() and the
+// following slp_restore_state() calls from
+// slp_switch()->g_switchstack() (which already needs to access it).
+//
+// However:
+//
+// that requires changing the prototypes and implementations of the
+// switching functions. If we just change the prototype of
+// slp_switch() to accept the argument and update the macros, without
+// changing the implementation of slp_switch(), we get crashes on
+// 64-bit Linux and 32-bit x86 (for reasons that aren't 100% clear);
+// on the other hand, 64-bit macOS seems to be fine. Also, 64-bit
+// windows is an issue because slp_switch is written fully in assembly
+// and currently ignores its argument so some code would have to be
+// adjusted there to pass the argument on to the
+// ``slp_save_state_asm()`` function (but interestingly, because of
+// the calling convention, the extra argument is just ignored and
+// things function fine, albeit slower, if we just modify
+// ``slp_save_state_asm`()` to fetch the pointer to pass to the
+// macro.)
+//
+// Our compromise is to use a *glabal*, untracked, weak, pointer
+// to the necessary thread state during the process of switching only.
+// This is safe because we're protected by the GIL, and if we're
+// running this code, the thread isn't exiting. This also nets us a
+// 10-12% speed improvement.
+
+static greenlet::Greenlet* volatile switching_thread_state = nullptr;
+
+
+extern "C" {
+static int GREENLET_NOINLINE(slp_save_state_trampoline)(char* stackref);
+static void GREENLET_NOINLINE(slp_restore_state_trampoline)();
+}
+
+
+#define SLP_SAVE_STATE(stackref, stsizediff) \
+do { \
+ assert(switching_thread_state); \
+ stackref += STACK_MAGIC; \
+ if (slp_save_state_trampoline((char*)stackref)) \
+ return -1; \
+ if (!switching_thread_state->active()) \
+ return 1; \
+ stsizediff = switching_thread_state->stack_start() - (char*)stackref; \
+} while (0)
+
+#define SLP_RESTORE_STATE() slp_restore_state_trampoline()
+
+#define SLP_EVAL
+extern "C" {
+#define slp_switch GREENLET_NOINLINE(slp_switch)
+#include "slp_platformselect.h"
+}
+#undef slp_switch
+
+#ifndef STACK_MAGIC
+# error \
+ "greenlet needs to be ported to this platform, or taught how to detect your compiler properly."
+#endif /* !STACK_MAGIC */
+
+
+
+#ifdef EXTERNAL_ASM
+/* CCP addition: Make these functions, to be called from assembler.
+ * The token include file for the given platform should enable the
+ * EXTERNAL_ASM define so that this is included.
+ */
+extern "C" {
+intptr_t
+slp_save_state_asm(intptr_t* ref)
+{
+ intptr_t diff;
+ SLP_SAVE_STATE(ref, diff);
+ return diff;
+}
+
+void
+slp_restore_state_asm(void)
+{
+ SLP_RESTORE_STATE();
+}
+
+extern int slp_switch(void);
+};
+#endif
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_thread_state.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_thread_state.hpp
new file mode 100644
index 000000000..045371f80
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_thread_state.hpp
@@ -0,0 +1,543 @@
+#ifndef GREENLET_THREAD_STATE_HPP
+#define GREENLET_THREAD_STATE_HPP
+
+#include
+#include
+
+#include "greenlet_internal.hpp"
+#include "greenlet_refs.hpp"
+#include "greenlet_thread_support.hpp"
+
+using greenlet::refs::BorrowedObject;
+using greenlet::refs::BorrowedGreenlet;
+using greenlet::refs::BorrowedMainGreenlet;
+using greenlet::refs::OwnedMainGreenlet;
+using greenlet::refs::OwnedObject;
+using greenlet::refs::OwnedGreenlet;
+using greenlet::refs::OwnedList;
+using greenlet::refs::PyErrFetchParam;
+using greenlet::refs::PyArgParseParam;
+using greenlet::refs::ImmortalString;
+using greenlet::refs::CreatedModule;
+using greenlet::refs::PyErrPieces;
+using greenlet::refs::NewReference;
+
+namespace greenlet {
+/**
+ * Thread-local state of greenlets.
+ *
+ * Each native thread will get exactly one of these objects,
+ * automatically accessed through the best available thread-local
+ * mechanism the compiler supports (``thread_local`` for C++11
+ * compilers or ``__thread``/``declspec(thread)`` for older GCC/clang
+ * or MSVC, respectively.)
+ *
+ * Previously, we kept thread-local state mostly in a bunch of
+ * ``static volatile`` variables in the main greenlet file.. This had
+ * the problem of requiring extra checks, loops, and great care
+ * accessing these variables if we potentially invoked any Python code
+ * that could release the GIL, because the state could change out from
+ * under us. Making the variables thread-local solves this problem.
+ *
+ * When we detected that a greenlet API accessing the current greenlet
+ * was invoked from a different thread than the greenlet belonged to,
+ * we stored a reference to the greenlet in the Python thread
+ * dictionary for the thread the greenlet belonged to. This could lead
+ * to memory leaks if the thread then exited (because of a reference
+ * cycle, as greenlets referred to the thread dictionary, and deleting
+ * non-current greenlets leaked their frame plus perhaps arguments on
+ * the C stack). If a thread exited while still having running
+ * greenlet objects (perhaps that had just switched back to the main
+ * greenlet), and did not invoke one of the greenlet APIs *in that
+ * thread, immediately before it exited, without some other thread
+ * then being invoked*, such a leak was guaranteed.
+ *
+ * This can be partly solved by using compiler thread-local variables
+ * instead of the Python thread dictionary, thus avoiding a cycle.
+ *
+ * To fully solve this problem, we need a reliable way to know that a
+ * thread is done and we should clean up the main greenlet. On POSIX,
+ * we can use the destructor function of ``pthread_key_create``, but
+ * there's nothing similar on Windows; a C++11 thread local object
+ * reliably invokes its destructor when the thread it belongs to exits
+ * (non-C++11 compilers offer ``__thread`` or ``declspec(thread)`` to
+ * create thread-local variables, but they can't hold C++ objects that
+ * invoke destructors; the C++11 version is the most portable solution
+ * I found). When the thread exits, we can drop references and
+ * otherwise manipulate greenlets and frames that we know can no
+ * longer be switched to. For compilers that don't support C++11
+ * thread locals, we have a solution that uses the python thread
+ * dictionary, though it may not collect everything as promptly as
+ * other compilers do, if some other library is using the thread
+ * dictionary and has a cycle or extra reference.
+ *
+ * There are two small wrinkles. The first is that when the thread
+ * exits, it is too late to actually invoke Python APIs: the Python
+ * thread state is gone, and the GIL is released. To solve *this*
+ * problem, our destructor uses ``Py_AddPendingCall`` to transfer the
+ * destruction work to the main thread. (This is not an issue for the
+ * dictionary solution.)
+ *
+ * The second is that once the thread exits, the thread local object
+ * is invalid and we can't even access a pointer to it, so we can't
+ * pass it to ``Py_AddPendingCall``. This is handled by actually using
+ * a second object that's thread local (ThreadStateCreator) and having
+ * it dynamically allocate this object so it can live until the
+ * pending call runs.
+ */
+
+
+
+class ThreadState {
+private:
+ // As of commit 08ad1dd7012b101db953f492e0021fb08634afad
+ // this class needed 56 bytes in o Py_DEBUG build
+ // on 64-bit macOS 11.
+ // Adding the vector takes us up to 80 bytes ()
+
+ /* Strong reference to the main greenlet */
+ OwnedMainGreenlet main_greenlet;
+
+ /* Strong reference to the current greenlet. */
+ OwnedGreenlet current_greenlet;
+
+ /* Strong reference to the trace function, if any. */
+ OwnedObject tracefunc;
+
+ typedef std::vector > deleteme_t;
+ /* A vector of raw PyGreenlet pointers representing things that need
+ deleted when this thread is running. The vector owns the
+ references, but you need to manually INCREF/DECREF as you use
+ them. We don't use a vector because we
+ make copy of this vector, and that would become O(n) as all the
+ refcounts are incremented in the copy.
+ */
+ deleteme_t deleteme;
+
+#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED
+ void* exception_state;
+#endif
+
+ static std::clock_t _clocks_used_doing_gc;
+ static ImmortalString get_referrers_name;
+ static PythonAllocator allocator;
+
+ G_NO_COPIES_OF_CLS(ThreadState);
+
+public:
+ static void* operator new(size_t UNUSED(count))
+ {
+ return ThreadState::allocator.allocate(1);
+ }
+
+ static void operator delete(void* ptr)
+ {
+ return ThreadState::allocator.deallocate(static_cast(ptr),
+ 1);
+ }
+
+ static void init()
+ {
+ ThreadState::get_referrers_name = "get_referrers";
+ ThreadState::_clocks_used_doing_gc = 0;
+ }
+
+ ThreadState()
+ : main_greenlet(OwnedMainGreenlet::consuming(green_create_main(this))),
+ current_greenlet(main_greenlet)
+ {
+ if (!this->main_greenlet) {
+ // We failed to create the main greenlet. That's bad.
+ throw PyFatalError("Failed to create main greenlet");
+ }
+ // The main greenlet starts with 1 refs: The returned one. We
+ // then copied it to the current greenlet.
+ assert(this->main_greenlet.REFCNT() == 2);
+
+#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED
+ this->exception_state = slp_get_exception_state();
+#endif
+ }
+
+ inline void restore_exception_state()
+ {
+#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED
+ // It's probably important this be inlined and only call C
+ // functions to avoid adding an SEH frame.
+ slp_set_exception_state(this->exception_state);
+#endif
+ }
+
+ inline bool has_main_greenlet()
+ {
+ return !!this->main_greenlet;
+ }
+
+ // Called from the ThreadStateCreator when we're in non-standard
+ // threading mode. In that case, there is an object in the Python
+ // thread state dictionary that points to us. The main greenlet
+ // also traverses into us, in which case it's crucial not to
+ // traverse back into the main greenlet.
+ int tp_traverse(visitproc visit, void* arg, bool traverse_main=true)
+ {
+ if (traverse_main) {
+ Py_VISIT(main_greenlet.borrow_o());
+ }
+ if (traverse_main || current_greenlet != main_greenlet) {
+ Py_VISIT(current_greenlet.borrow_o());
+ }
+ Py_VISIT(tracefunc.borrow());
+ return 0;
+ }
+
+ inline BorrowedMainGreenlet borrow_main_greenlet() const
+ {
+ assert(this->main_greenlet);
+ assert(this->main_greenlet.REFCNT() >= 2);
+ return this->main_greenlet;
+ };
+
+ inline OwnedMainGreenlet get_main_greenlet()
+ {
+ return this->main_greenlet;
+ }
+
+ /**
+ * In addition to returning a new reference to the currunt
+ * greenlet, this performs any maintenance needed.
+ */
+ inline OwnedGreenlet get_current()
+ {
+ /* green_dealloc() cannot delete greenlets from other threads, so
+ it stores them in the thread dict; delete them now. */
+ this->clear_deleteme_list();
+ //assert(this->current_greenlet->main_greenlet == this->main_greenlet);
+ //assert(this->main_greenlet->main_greenlet == this->main_greenlet);
+ return this->current_greenlet;
+ }
+
+ /**
+ * As for non-const get_current();
+ */
+ inline BorrowedGreenlet borrow_current()
+ {
+ this->clear_deleteme_list();
+ return this->current_greenlet;
+ }
+
+ /**
+ * Does no maintenance.
+ */
+ inline OwnedGreenlet get_current() const
+ {
+ return this->current_greenlet;
+ }
+
+ template
+ inline bool is_current(const refs::PyObjectPointer& obj) const
+ {
+ return this->current_greenlet.borrow_o() == obj.borrow_o();
+ }
+
+ inline void set_current(const OwnedGreenlet& target)
+ {
+ this->current_greenlet = target;
+ }
+
+private:
+ /**
+ * Deref and remove the greenlets from the deleteme list. Must be
+ * holding the GIL.
+ *
+ * If *murder* is true, then we must be called from a different
+ * thread than the one that these greenlets were running in.
+ * In that case, if the greenlet was actually running, we destroy
+ * the frame reference and otherwise make it appear dead before
+ * proceeding; otherwise, we would try (and fail) to raise an
+ * exception in it and wind up right back in this list.
+ */
+ inline void clear_deleteme_list(const bool murder=false)
+ {
+ if (!this->deleteme.empty()) {
+ // It's possible we could add items to this list while
+ // running Python code if there's a thread switch, so we
+ // need to defensively copy it before that can happen.
+ deleteme_t copy = this->deleteme;
+ this->deleteme.clear(); // in case things come back on the list
+ for(deleteme_t::iterator it = copy.begin(), end = copy.end();
+ it != end;
+ ++it ) {
+ PyGreenlet* to_del = *it;
+ if (murder) {
+ // Force each greenlet to appear dead; we can't raise an
+ // exception into it anymore anyway.
+ to_del->pimpl->murder_in_place();
+ }
+
+ // The only reference to these greenlets should be in
+ // this list, decreffing them should let them be
+ // deleted again, triggering calls to green_dealloc()
+ // in the correct thread (if we're not murdering).
+ // This may run arbitrary Python code and switch
+ // threads or greenlets!
+ Py_DECREF(to_del);
+ if (PyErr_Occurred()) {
+ PyErr_WriteUnraisable(nullptr);
+ PyErr_Clear();
+ }
+ }
+ }
+ }
+
+public:
+
+ /**
+ * Returns a new reference, or a false object.
+ */
+ inline OwnedObject get_tracefunc() const
+ {
+ return tracefunc;
+ };
+
+
+ inline void set_tracefunc(BorrowedObject tracefunc)
+ {
+ assert(tracefunc);
+ if (tracefunc == BorrowedObject(Py_None)) {
+ this->tracefunc.CLEAR();
+ }
+ else {
+ this->tracefunc = tracefunc;
+ }
+ }
+
+ /**
+ * Given a reference to a greenlet that some other thread
+ * attempted to delete (has a refcount of 0) store it for later
+ * deletion when the thread this state belongs to is current.
+ */
+ inline void delete_when_thread_running(PyGreenlet* to_del)
+ {
+ Py_INCREF(to_del);
+ this->deleteme.push_back(to_del);
+ }
+
+ /**
+ * Set to std::clock_t(-1) to disable.
+ */
+ inline static std::clock_t& clocks_used_doing_gc()
+ {
+ return ThreadState::_clocks_used_doing_gc;
+ }
+
+ ~ThreadState()
+ {
+ if (!PyInterpreterState_Head()) {
+ // We shouldn't get here (our callers protect us)
+ // but if we do, all we can do is bail early.
+ return;
+ }
+
+ // We should not have an "origin" greenlet; that only exists
+ // for the temporary time during a switch, which should not
+ // be in progress as the thread dies.
+ //assert(!this->switching_state.origin);
+
+ this->tracefunc.CLEAR();
+
+ // Forcibly GC as much as we can.
+ this->clear_deleteme_list(true);
+
+ // The pending call did this.
+ assert(this->main_greenlet->thread_state() == nullptr);
+
+ // If the main greenlet is the current greenlet,
+ // then we "fell off the end" and the thread died.
+ // It's possible that there is some other greenlet that
+ // switched to us, leaving a reference to the main greenlet
+ // on the stack, somewhere uncollectible. Try to detect that.
+ if (this->current_greenlet == this->main_greenlet && this->current_greenlet) {
+ assert(this->current_greenlet->is_currently_running_in_some_thread());
+ // Drop one reference we hold.
+ this->current_greenlet.CLEAR();
+ assert(!this->current_greenlet);
+ // Only our reference to the main greenlet should be left,
+ // But hold onto the pointer in case we need to do extra cleanup.
+ PyGreenlet* old_main_greenlet = this->main_greenlet.borrow();
+ Py_ssize_t cnt = this->main_greenlet.REFCNT();
+ this->main_greenlet.CLEAR();
+ if (ThreadState::_clocks_used_doing_gc != std::clock_t(-1)
+ && cnt == 2 && Py_REFCNT(old_main_greenlet) == 1) {
+ // Highly likely that the reference is somewhere on
+ // the stack, not reachable by GC. Verify.
+ // XXX: This is O(n) in the total number of objects.
+ // TODO: Add a way to disable this at runtime, and
+ // another way to report on it.
+ std::clock_t begin = std::clock();
+ NewReference gc(PyImport_ImportModule("gc"));
+ if (gc) {
+ OwnedObject get_referrers = gc.PyRequireAttr(ThreadState::get_referrers_name);
+ OwnedList refs(get_referrers.PyCall(old_main_greenlet));
+ if (refs && refs.empty()) {
+ assert(refs.REFCNT() == 1);
+ // We found nothing! So we left a dangling
+ // reference: Probably the last thing some
+ // other greenlet did was call
+ // 'getcurrent().parent.switch()' to switch
+ // back to us. Clean it up. This will be the
+ // case on CPython 3.7 and newer, as they use
+ // an internal calling conversion that avoids
+ // creating method objects and storing them on
+ // the stack.
+ Py_DECREF(old_main_greenlet);
+ }
+ else if (refs
+ && refs.size() == 1
+ && PyCFunction_Check(refs.at(0))
+ && Py_REFCNT(refs.at(0)) == 2) {
+ assert(refs.REFCNT() == 1);
+ // Ok, we found a C method that refers to the
+ // main greenlet, and its only referenced
+ // twice, once in the list we just created,
+ // once from...somewhere else. If we can't
+ // find where else, then this is a leak.
+ // This happens in older versions of CPython
+ // that create a bound method object somewhere
+ // on the stack that we'll never get back to.
+ if (PyCFunction_GetFunction(refs.at(0).borrow()) == (PyCFunction)green_switch) {
+ BorrowedObject function_w = refs.at(0);
+ refs.clear(); // destroy the reference
+ // from the list.
+ // back to one reference. Can *it* be
+ // found?
+ assert(function_w.REFCNT() == 1);
+ refs = get_referrers.PyCall(function_w);
+ if (refs && refs.empty()) {
+ // Nope, it can't be found so it won't
+ // ever be GC'd. Drop it.
+ Py_CLEAR(function_w);
+ }
+ }
+ }
+ std::clock_t end = std::clock();
+ ThreadState::_clocks_used_doing_gc += (end - begin);
+ }
+ }
+ }
+
+ // We need to make sure this greenlet appears to be dead,
+ // because otherwise deallocing it would fail to raise an
+ // exception in it (the thread is dead) and put it back in our
+ // deleteme list.
+ if (this->current_greenlet) {
+ this->current_greenlet->murder_in_place();
+ this->current_greenlet.CLEAR();
+ }
+
+ if (this->main_greenlet) {
+ // Couldn't have been the main greenlet that was running
+ // when the thread exited (because we already cleared this
+ // pointer if it was). This shouldn't be possible?
+
+ // If the main greenlet was current when the thread died (it
+ // should be, right?) then we cleared its self pointer above
+ // when we cleared the current greenlet's main greenlet pointer.
+ // assert(this->main_greenlet->main_greenlet == this->main_greenlet
+ // || !this->main_greenlet->main_greenlet);
+ // // self reference, probably gone
+ // this->main_greenlet->main_greenlet.CLEAR();
+
+ // This will actually go away when the ivar is destructed.
+ this->main_greenlet.CLEAR();
+ }
+
+ if (PyErr_Occurred()) {
+ PyErr_WriteUnraisable(NULL);
+ PyErr_Clear();
+ }
+
+ }
+
+};
+
+ImmortalString ThreadState::get_referrers_name(nullptr);
+PythonAllocator ThreadState::allocator;
+std::clock_t ThreadState::_clocks_used_doing_gc(0);
+
+template
+class ThreadStateCreator
+{
+private:
+ // Initialized to 1, and, if still 1, created on access.
+ // Set to 0 on destruction.
+ ThreadState* _state;
+ G_NO_COPIES_OF_CLS(ThreadStateCreator);
+public:
+
+ // Only one of these, auto created per thread
+ ThreadStateCreator() :
+ _state((ThreadState*)1)
+ {
+ }
+
+ ~ThreadStateCreator()
+ {
+ ThreadState* tmp = this->_state;
+ this->_state = nullptr;
+ if (tmp && tmp != (ThreadState*)1) {
+ Destructor x(tmp);
+ }
+ }
+
+ inline ThreadState& state()
+ {
+ // The main greenlet will own this pointer when it is created,
+ // which will be right after this. The plan is to give every
+ // greenlet a pointer to the main greenlet for the thread it
+ // runs in; if we are doing something cross-thread, we need to
+ // access the pointer from the main greenlet. Deleting the
+ // thread, and hence the thread-local storage, will delete the
+ // state pointer in the main greenlet.
+ if (this->_state == (ThreadState*)1) {
+ // XXX: Assuming allocation never fails
+ this->_state = new ThreadState;
+ // For non-standard threading, we need to store an object
+ // in the Python thread state dictionary so that it can be
+ // DECREF'd when the thread ends (ideally; the dict could
+ // last longer) and clean this object up.
+ }
+ if (!this->_state) {
+ throw std::runtime_error("Accessing state after destruction.");
+ }
+ return *this->_state;
+ }
+
+ operator ThreadState&()
+ {
+ return this->state();
+ }
+
+ operator ThreadState*()
+ {
+ return &this->state();
+ }
+
+ inline int tp_traverse(visitproc visit, void* arg)
+ {
+ if (this->_state) {
+ return this->_state->tp_traverse(visit, arg);
+ }
+ return 0;
+ }
+
+};
+
+
+// We can't use the PythonAllocator for this, because we push to it
+// from the thread state destructor, which doesn't have the GIL,
+// and Python's allocators can only be called with the GIL.
+typedef std::vector cleanup_queue_t;
+
+}; // namespace greenlet
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_thread_state_dict_cleanup.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_thread_state_dict_cleanup.hpp
new file mode 100644
index 000000000..acf39c8f4
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_thread_state_dict_cleanup.hpp
@@ -0,0 +1,118 @@
+#ifndef GREENLET_THREAD_STATE_DICT_CLEANUP_HPP
+#define GREENLET_THREAD_STATE_DICT_CLEANUP_HPP
+
+#include "greenlet_internal.hpp"
+#include "greenlet_thread_state.hpp"
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+#ifndef G_THREAD_STATE_DICT_CLEANUP_TYPE
+// shut the compiler up if it looks at this file in isolation
+#define ThreadStateCreator int
+#endif
+
+// Define a Python object that goes in the Python thread state dict
+// when the greenlet thread state is created, and which owns the
+// reference to the greenlet thread local state.
+// When the thread state dict is cleaned up, so too is the thread
+// state. This works best if we make sure there are no circular
+// references to the thread state.
+typedef struct _PyGreenletCleanup {
+ PyObject_HEAD
+ ThreadStateCreator* thread_state_creator;
+} PyGreenletCleanup;
+
+static void
+cleanup_do_dealloc(PyGreenletCleanup* self)
+{
+ ThreadStateCreator* tmp = self->thread_state_creator;
+ self->thread_state_creator = nullptr;
+ if (tmp) {
+ delete tmp;
+ }
+}
+
+static void
+cleanup_dealloc(PyGreenletCleanup* self)
+{
+ PyObject_GC_UnTrack(self);
+ cleanup_do_dealloc(self);
+}
+
+static int
+cleanup_clear(PyGreenletCleanup* self)
+{
+ // This method is never called by our test cases.
+ cleanup_do_dealloc(self);
+ return 0;
+}
+
+static int
+cleanup_traverse(PyGreenletCleanup* self, visitproc visit, void* arg)
+{
+ if (self->thread_state_creator) {
+ return self->thread_state_creator->tp_traverse(visit, arg);
+ }
+ return 0;
+}
+
+static int
+cleanup_is_gc(PyGreenlet* UNUSED(self))
+{
+ return 1;
+}
+
+static PyTypeObject PyGreenletCleanup_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "greenlet._greenlet.ThreadStateCleanup",
+ sizeof(struct _PyGreenletCleanup),
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)cleanup_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as _number*/
+ 0, /* tp_as _sequence*/
+ 0, /* tp_as _mapping*/
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer*/
+ G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "Internal use only", /* tp_doc */
+ (traverseproc)cleanup_traverse, /* tp_traverse */
+ (inquiry)cleanup_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ // XXX: Don't our flags promise a weakref?
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ PyType_GenericAlloc, /* tp_alloc */
+ PyType_GenericNew, /* tp_new */
+ PyObject_GC_Del, /* tp_free */
+ (inquiry)cleanup_is_gc, /* tp_is_gc */
+};
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/greenlet_thread_support.hpp b/venv/lib/python3.10/site-packages/greenlet/greenlet_thread_support.hpp
new file mode 100644
index 000000000..3ded7d2b7
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/greenlet_thread_support.hpp
@@ -0,0 +1,31 @@
+#ifndef GREENLET_THREAD_SUPPORT_HPP
+#define GREENLET_THREAD_SUPPORT_HPP
+
+/**
+ * Defines various utility functions to help greenlet integrate well
+ * with threads. This used to be needed when we supported Python
+ * 2.7 on Windows, which used a very old compiler. We wrote an
+ * alternative implementation using Python APIs and POSIX or Windows
+ * APIs, but that's no longer needed. So this file is a shadow of its
+ * former self --- but may be needed in the future.
+ */
+
+#include
+#include
+#include
+
+#include "greenlet_compiler_compat.hpp"
+
+namespace greenlet {
+ typedef std::mutex Mutex;
+ typedef std::lock_guard LockGuard;
+ class LockInitError : public std::runtime_error
+ {
+ public:
+ LockInitError(const char* what) : std::runtime_error(what)
+ {};
+ };
+};
+
+
+#endif /* GREENLET_THREAD_SUPPORT_HPP */
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/__init__.py b/venv/lib/python3.10/site-packages/greenlet/platform/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/greenlet/platform/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 000000000..7bfe99cc1
Binary files /dev/null and b/venv/lib/python3.10/site-packages/greenlet/platform/__pycache__/__init__.cpython-310.pyc differ
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/setup_switch_x64_masm.cmd b/venv/lib/python3.10/site-packages/greenlet/platform/setup_switch_x64_masm.cmd
new file mode 100644
index 000000000..092859555
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/setup_switch_x64_masm.cmd
@@ -0,0 +1,2 @@
+call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" amd64
+ml64 /nologo /c /Fo switch_x64_masm.obj switch_x64_masm.asm
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_aarch64_gcc.h b/venv/lib/python3.10/site-packages/greenlet/platform/switch_aarch64_gcc.h
new file mode 100644
index 000000000..058617c40
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_aarch64_gcc.h
@@ -0,0 +1,124 @@
+/*
+ * this is the internal transfer function.
+ *
+ * HISTORY
+ * 07-Sep-16 Add clang support using x register naming. Fredrik Fornwall
+ * 13-Apr-13 Add support for strange GCC caller-save decisions
+ * 08-Apr-13 File creation. Michael Matz
+ *
+ * NOTES
+ *
+ * Simply save all callee saved registers
+ *
+ */
+
+#define STACK_REFPLUS 1
+
+#ifdef SLP_EVAL
+#define STACK_MAGIC 0
+#define REGS_TO_SAVE "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", \
+ "x27", "x28", "x30" /* aka lr */, \
+ "v8", "v9", "v10", "v11", \
+ "v12", "v13", "v14", "v15"
+
+/*
+ * Recall:
+ asm asm-qualifiers ( AssemblerTemplate
+ : OutputOperands
+ [ : InputOperands
+ [ : Clobbers ] ])
+
+ or (if asm-qualifiers contains 'goto')
+
+ asm asm-qualifiers ( AssemblerTemplate
+ : OutputOperands
+ : InputOperands
+ : Clobbers
+ : GotoLabels)
+
+ and OutputOperands are
+
+ [ [asmSymbolicName] ] constraint (cvariablename)
+
+ When a name is given, refer to it as ``%[the name]``.
+ When not given, ``%i`` where ``i`` is the zero-based index.
+
+ constraints starting with ``=`` means only writing; ``+`` means
+ reading and writing.
+
+ This is followed by ``r`` (must be register) or ``m`` (must be memory)
+ and these can be combined.
+
+ The ``cvariablename`` is actually an lvalue expression.
+
+ In AArch65, 31 general purpose registers. If named X0... they are
+ 64-bit. If named W0... they are the bottom 32 bits of the
+ corresponding 64 bit register.
+
+ XZR and WZR are hardcoded to 0, and ignore writes.
+
+ Arguments are in X0..X7. C++ uses X0 for ``this``. X0 holds simple return
+ values (?)
+
+ Whenever a W register is written, the top half of the X register is zeroed.
+ */
+
+static int
+slp_switch(void)
+{
+ int err;
+ void *fp;
+ /* Windowz uses a 32-bit long on a 64-bit platform, unlike the rest of
+ the world, and in theory we can be compiled with GCC/llvm on 64-bit
+ windows. So we need a fixed-width type.
+ */
+ int64_t *stackref, stsizediff;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ volatile ("str x29, %0" : "=m"(fp) : : );
+ __asm__ ("mov %0, sp" : "=r" (stackref));
+ {
+ SLP_SAVE_STATE(stackref, stsizediff);
+ __asm__ volatile (
+ "add sp,sp,%0\n"
+ "add x29,x29,%0\n"
+ :
+ : "r" (stsizediff)
+ );
+ SLP_RESTORE_STATE();
+ /* SLP_SAVE_STATE macro contains some return statements
+ (of -1 and 1). It falls through only when
+ the return value of slp_save_state() is zero, which
+ is placed in x0.
+ In that case we (slp_switch) also want to return zero
+ (also in x0 of course).
+ Now, some GCC versions (seen with 4.8) think it's a
+ good idea to save/restore x0 around the call to
+ slp_restore_state(), instead of simply zeroing it
+ at the return below. But slp_restore_state
+ writes random values to the stack slot used for this
+ save/restore (from when it once was saved above in
+ SLP_SAVE_STATE, when it was still uninitialized), so
+ "restoring" that precious zero actually makes us
+ return random values. There are some ways to make
+ GCC not use that zero value in the normal return path
+ (e.g. making err volatile, but that costs a little
+ stack space), and the simplest is to call a function
+ that returns an unknown value (which happens to be zero),
+ so the saved/restored value is unused.
+
+ Thus, this line stores a 0 into the ``err`` variable
+ (which must be held in a register for this instruction,
+ of course). The ``w`` qualifier causes the instruction
+ to use W0 instead of X0, otherwise we get a warning
+ about a value size mismatch (because err is an int,
+ and aarch64 platforms are LP64: 32-bit int, 64 bit long
+ and pointer).
+ */
+ __asm__ volatile ("mov %w0, #0" : "=r" (err));
+ }
+ __asm__ volatile ("ldr x29, %0" : : "m" (fp) :);
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ return err;
+}
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_alpha_unix.h b/venv/lib/python3.10/site-packages/greenlet/platform/switch_alpha_unix.h
new file mode 100644
index 000000000..7e07abfc2
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_alpha_unix.h
@@ -0,0 +1,30 @@
+#define STACK_REFPLUS 1
+
+#ifdef SLP_EVAL
+#define STACK_MAGIC 0
+
+#define REGS_TO_SAVE "$9", "$10", "$11", "$12", "$13", "$14", "$15", \
+ "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9"
+
+static int
+slp_switch(void)
+{
+ int ret;
+ long *stackref, stsizediff;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ volatile ("mov $30, %0" : "=r" (stackref) : );
+ {
+ SLP_SAVE_STATE(stackref, stsizediff);
+ __asm__ volatile (
+ "addq $30, %0, $30\n\t"
+ : /* no outputs */
+ : "r" (stsizediff)
+ );
+ SLP_RESTORE_STATE();
+ }
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ volatile ("mov $31, %0" : "=r" (ret) : );
+ return ret;
+}
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_amd64_unix.h b/venv/lib/python3.10/site-packages/greenlet/platform/switch_amd64_unix.h
new file mode 100644
index 000000000..d4701105f
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_amd64_unix.h
@@ -0,0 +1,87 @@
+/*
+ * this is the internal transfer function.
+ *
+ * HISTORY
+ * 3-May-13 Ralf Schmitt
+ * Add support for strange GCC caller-save decisions
+ * (ported from switch_aarch64_gcc.h)
+ * 18-Aug-11 Alexey Borzenkov
+ * Correctly save rbp, csr and cw
+ * 01-Apr-04 Hye-Shik Chang
+ * Ported from i386 to amd64.
+ * 24-Nov-02 Christian Tismer
+ * needed to add another magic constant to insure
+ * that f in slp_eval_frame(PyFrameObject *f)
+ * STACK_REFPLUS will probably be 1 in most cases.
+ * gets included into the saved stack area.
+ * 17-Sep-02 Christian Tismer
+ * after virtualizing stack save/restore, the
+ * stack size shrunk a bit. Needed to introduce
+ * an adjustment STACK_MAGIC per platform.
+ * 15-Sep-02 Gerd Woetzel
+ * slightly changed framework for spark
+ * 31-Avr-02 Armin Rigo
+ * Added ebx, esi and edi register-saves.
+ * 01-Mar-02 Samual M. Rushing
+ * Ported from i386.
+ */
+
+#define STACK_REFPLUS 1
+
+#ifdef SLP_EVAL
+
+/* #define STACK_MAGIC 3 */
+/* the above works fine with gcc 2.96, but 2.95.3 wants this */
+#define STACK_MAGIC 0
+
+#define REGS_TO_SAVE "r12", "r13", "r14", "r15"
+
+static int
+slp_switch(void)
+{
+ int err;
+ void* rbp;
+ void* rbx;
+ unsigned int csr;
+ unsigned short cw;
+ /* This used to be declared 'register', but that does nothing in
+ modern compilers and is explicitly forbidden in some new
+ standards. */
+ long *stackref, stsizediff;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ volatile ("fstcw %0" : "=m" (cw));
+ __asm__ volatile ("stmxcsr %0" : "=m" (csr));
+ __asm__ volatile ("movq %%rbp, %0" : "=m" (rbp));
+ __asm__ volatile ("movq %%rbx, %0" : "=m" (rbx));
+ __asm__ ("movq %%rsp, %0" : "=g" (stackref));
+ {
+ SLP_SAVE_STATE(stackref, stsizediff);
+ __asm__ volatile (
+ "addq %0, %%rsp\n"
+ "addq %0, %%rbp\n"
+ :
+ : "r" (stsizediff)
+ );
+ SLP_RESTORE_STATE();
+ __asm__ volatile ("xorq %%rax, %%rax" : "=a" (err));
+ }
+ __asm__ volatile ("movq %0, %%rbx" : : "m" (rbx));
+ __asm__ volatile ("movq %0, %%rbp" : : "m" (rbp));
+ __asm__ volatile ("ldmxcsr %0" : : "m" (csr));
+ __asm__ volatile ("fldcw %0" : : "m" (cw));
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ return err;
+}
+
+#endif
+
+/*
+ * further self-processing support
+ */
+
+/*
+ * if you want to add self-inspection tools, place them
+ * here. See the x86_msvc for the necessary defines.
+ * These features are highly experimental und not
+ * essential yet.
+ */
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_gcc.h b/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_gcc.h
new file mode 100644
index 000000000..655003aa1
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_gcc.h
@@ -0,0 +1,79 @@
+/*
+ * this is the internal transfer function.
+ *
+ * HISTORY
+ * 14-Aug-06 File creation. Ported from Arm Thumb. Sylvain Baro
+ * 3-Sep-06 Commented out saving of r1-r3 (r4 already commented out) as I
+ * read that these do not need to be saved. Also added notes and
+ * errors related to the frame pointer. Richard Tew.
+ *
+ * NOTES
+ *
+ * It is not possible to detect if fp is used or not, so the supplied
+ * switch function needs to support it, so that you can remove it if
+ * it does not apply to you.
+ *
+ * POSSIBLE ERRORS
+ *
+ * "fp cannot be used in asm here"
+ *
+ * - Try commenting out "fp" in REGS_TO_SAVE.
+ *
+ */
+
+#define STACK_REFPLUS 1
+
+#ifdef SLP_EVAL
+#define STACK_MAGIC 0
+#define REG_SP "sp"
+#define REG_SPSP "sp,sp"
+#ifdef __thumb__
+#define REG_FP "r7"
+#define REG_FPFP "r7,r7"
+#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r9", "r10", "r11", "lr"
+#else
+#define REG_FP "fp"
+#define REG_FPFP "fp,fp"
+#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r8", "r9", "r10", "lr"
+#endif
+#if defined(__SOFTFP__)
+#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL
+#elif defined(__VFP_FP__)
+#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \
+ "d12", "d13", "d14", "d15"
+#elif defined(__MAVERICK__)
+#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "mvf4", "mvf5", "mvf6", "mvf7", \
+ "mvf8", "mvf9", "mvf10", "mvf11", \
+ "mvf12", "mvf13", "mvf14", "mvf15"
+#else
+#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "f4", "f5", "f6", "f7"
+#endif
+
+static int
+#ifdef __GNUC__
+__attribute__((optimize("no-omit-frame-pointer")))
+#endif
+slp_switch(void)
+{
+ void *fp;
+ int *stackref, stsizediff;
+ int result;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ volatile ("mov r0," REG_FP "\n\tstr r0,%0" : "=m" (fp) : : "r0");
+ __asm__ ("mov %0," REG_SP : "=r" (stackref));
+ {
+ SLP_SAVE_STATE(stackref, stsizediff);
+ __asm__ volatile (
+ "add " REG_SPSP ",%0\n"
+ "add " REG_FPFP ",%0\n"
+ :
+ : "r" (stsizediff)
+ );
+ SLP_RESTORE_STATE();
+ }
+ __asm__ volatile ("ldr r0,%1\n\tmov " REG_FP ",r0\n\tmov %0, #0" : "=r" (result) : "m" (fp) : "r0");
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ return result;
+}
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_ios.h b/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_ios.h
new file mode 100644
index 000000000..9e640e15f
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_ios.h
@@ -0,0 +1,67 @@
+/*
+ * this is the internal transfer function.
+ *
+ * HISTORY
+ * 31-May-15 iOS support. Ported from arm32. Proton
+ *
+ * NOTES
+ *
+ * It is not possible to detect if fp is used or not, so the supplied
+ * switch function needs to support it, so that you can remove it if
+ * it does not apply to you.
+ *
+ * POSSIBLE ERRORS
+ *
+ * "fp cannot be used in asm here"
+ *
+ * - Try commenting out "fp" in REGS_TO_SAVE.
+ *
+ */
+
+#define STACK_REFPLUS 1
+
+#ifdef SLP_EVAL
+
+#define STACK_MAGIC 0
+#define REG_SP "sp"
+#define REG_SPSP "sp,sp"
+#define REG_FP "r7"
+#define REG_FPFP "r7,r7"
+#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r10", "r11", "lr"
+#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \
+ "d12", "d13", "d14", "d15"
+
+static int
+#ifdef __GNUC__
+__attribute__((optimize("no-omit-frame-pointer")))
+#endif
+slp_switch(void)
+{
+ void *fp;
+ int *stackref, stsizediff, result;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ volatile ("str " REG_FP ",%0" : "=m" (fp));
+ __asm__ ("mov %0," REG_SP : "=r" (stackref));
+ {
+ SLP_SAVE_STATE(stackref, stsizediff);
+ __asm__ volatile (
+ "add " REG_SPSP ",%0\n"
+ "add " REG_FPFP ",%0\n"
+ :
+ : "r" (stsizediff)
+ : REGS_TO_SAVE /* Clobber registers, force compiler to
+ * recalculate address of void *fp from REG_SP or REG_FP */
+ );
+ SLP_RESTORE_STATE();
+ }
+ __asm__ volatile (
+ "ldr " REG_FP ", %1\n\t"
+ "mov %0, #0"
+ : "=r" (result)
+ : "m" (fp)
+ : REGS_TO_SAVE /* Force compiler to restore saved registers after this */
+ );
+ return result;
+}
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.asm b/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.asm
new file mode 100644
index 000000000..29f9c225e
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.asm
@@ -0,0 +1,53 @@
+ AREA switch_arm64_masm, CODE, READONLY;
+ GLOBAL slp_switch [FUNC]
+ EXTERN slp_save_state_asm
+ EXTERN slp_restore_state_asm
+
+slp_switch
+ ; push callee saved registers to stack
+ stp x19, x20, [sp, #-16]!
+ stp x21, x22, [sp, #-16]!
+ stp x23, x24, [sp, #-16]!
+ stp x25, x26, [sp, #-16]!
+ stp x27, x28, [sp, #-16]!
+ stp x29, x30, [sp, #-16]!
+ stp d8, d9, [sp, #-16]!
+ stp d10, d11, [sp, #-16]!
+ stp d12, d13, [sp, #-16]!
+ stp d14, d15, [sp, #-16]!
+
+ ; call slp_save_state_asm with stack pointer
+ mov x0, sp
+ bl slp_save_state_asm
+
+ ; early return for return value of 1 and -1
+ cmp x0, #-1
+ b.eq RETURN
+ cmp x0, #1
+ b.eq RETURN
+
+ ; increment stack and frame pointer
+ add sp, sp, x0
+ add x29, x29, x0
+
+ bl slp_restore_state_asm
+
+ ; store return value for successful completion of routine
+ mov x0, #0
+
+RETURN
+ ; pop registers from stack
+ ldp d14, d15, [sp], #16
+ ldp d12, d13, [sp], #16
+ ldp d10, d11, [sp], #16
+ ldp d8, d9, [sp], #16
+ ldp x29, x30, [sp], #16
+ ldp x27, x28, [sp], #16
+ ldp x25, x26, [sp], #16
+ ldp x23, x24, [sp], #16
+ ldp x21, x22, [sp], #16
+ ldp x19, x20, [sp], #16
+
+ ret
+
+ END
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.obj b/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.obj
new file mode 100644
index 000000000..f6f220e43
Binary files /dev/null and b/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.obj differ
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_msvc.h b/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_msvc.h
new file mode 100644
index 000000000..7ab7f45be
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_msvc.h
@@ -0,0 +1,17 @@
+/*
+ * this is the internal transfer function.
+ *
+ * HISTORY
+ * 21-Oct-21 Niyas Sait
+ * First version to enable win/arm64 support.
+ */
+
+#define STACK_REFPLUS 1
+#define STACK_MAGIC 0
+
+/* Use the generic support for an external assembly language slp_switch function. */
+#define EXTERNAL_ASM
+
+#ifdef SLP_EVAL
+/* This always uses the external masm assembly file. */
+#endif
\ No newline at end of file
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_csky_gcc.h b/venv/lib/python3.10/site-packages/greenlet/platform/switch_csky_gcc.h
new file mode 100644
index 000000000..ac469d3a0
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_csky_gcc.h
@@ -0,0 +1,48 @@
+#ifdef SLP_EVAL
+#define STACK_MAGIC 0
+#define REG_FP "r8"
+#ifdef __CSKYABIV2__
+#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r9", "r10", "r11", "r15",\
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22",\
+ "r23", "r24", "r25"
+
+#if defined (__CSKY_HARD_FLOAT__) || (__CSKY_VDSP__)
+#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "vr8", "vr9", "vr10", "vr11", "vr12",\
+ "vr13", "vr14", "vr15"
+#else
+#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL
+#endif
+#else
+#define REGS_TO_SAVE "r9", "r10", "r11", "r12", "r13", "r15"
+#endif
+
+
+static int
+#ifdef __GNUC__
+__attribute__((optimize("no-omit-frame-pointer")))
+#endif
+slp_switch(void)
+{
+ int *stackref, stsizediff;
+ int result;
+
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ ("mov %0, sp" : "=r" (stackref));
+ {
+ SLP_SAVE_STATE(stackref, stsizediff);
+ __asm__ volatile (
+ "addu sp,%0\n"
+ "addu "REG_FP",%0\n"
+ :
+ : "r" (stsizediff)
+ );
+
+ SLP_RESTORE_STATE();
+ }
+ __asm__ volatile ("movi %0, 0" : "=r" (result));
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+
+ return result;
+}
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_loongarch64_linux.h b/venv/lib/python3.10/site-packages/greenlet/platform/switch_loongarch64_linux.h
new file mode 100644
index 000000000..9eaf34ef4
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_loongarch64_linux.h
@@ -0,0 +1,31 @@
+#define STACK_REFPLUS 1
+
+#ifdef SLP_EVAL
+#define STACK_MAGIC 0
+
+#define REGS_TO_SAVE "s0", "s1", "s2", "s3", "s4", "s5", \
+ "s6", "s7", "s8", "fp", \
+ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31"
+
+static int
+slp_switch(void)
+{
+ int ret;
+ long *stackref, stsizediff;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ volatile ("move %0, $sp" : "=r" (stackref) : );
+ {
+ SLP_SAVE_STATE(stackref, stsizediff);
+ __asm__ volatile (
+ "add.d $sp, $sp, %0\n\t"
+ : /* no outputs */
+ : "r" (stsizediff)
+ );
+ SLP_RESTORE_STATE();
+ }
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ volatile ("move %0, $zero" : "=r" (ret) : );
+ return ret;
+}
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_m68k_gcc.h b/venv/lib/python3.10/site-packages/greenlet/platform/switch_m68k_gcc.h
new file mode 100644
index 000000000..da761c2da
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_m68k_gcc.h
@@ -0,0 +1,38 @@
+/*
+ * this is the internal transfer function.
+ *
+ * HISTORY
+ * 2014-01-06 Andreas Schwab
+ * File created.
+ */
+
+#ifdef SLP_EVAL
+
+#define STACK_MAGIC 0
+
+#define REGS_TO_SAVE "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", \
+ "%a2", "%a3", "%a4"
+
+static int
+slp_switch(void)
+{
+ int err;
+ int *stackref, stsizediff;
+ void *fp, *a5;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ volatile ("move.l %%fp, %0" : "=m"(fp));
+ __asm__ volatile ("move.l %%a5, %0" : "=m"(a5));
+ __asm__ ("move.l %%sp, %0" : "=r"(stackref));
+ {
+ SLP_SAVE_STATE(stackref, stsizediff);
+ __asm__ volatile ("add.l %0, %%sp; add.l %0, %%fp" : : "r"(stsizediff));
+ SLP_RESTORE_STATE();
+ __asm__ volatile ("clr.l %0" : "=g" (err));
+ }
+ __asm__ volatile ("move.l %0, %%a5" : : "m"(a5));
+ __asm__ volatile ("move.l %0, %%fp" : : "m"(fp));
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ return err;
+}
+
+#endif
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_mips_unix.h b/venv/lib/python3.10/site-packages/greenlet/platform/switch_mips_unix.h
new file mode 100644
index 000000000..b9003e947
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_mips_unix.h
@@ -0,0 +1,64 @@
+/*
+ * this is the internal transfer function.
+ *
+ * HISTORY
+ * 20-Sep-14 Matt Madison
+ * Re-code the saving of the gp register for MIPS64.
+ * 05-Jan-08 Thiemo Seufer
+ * Ported from ppc.
+ */
+
+#define STACK_REFPLUS 1
+
+#ifdef SLP_EVAL
+
+#define STACK_MAGIC 0
+
+#define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \
+ "$23", "$30"
+static int
+slp_switch(void)
+{
+ int err;
+ int *stackref, stsizediff;
+#ifdef __mips64
+ uint64_t gpsave;
+#endif
+ __asm__ __volatile__ ("" : : : REGS_TO_SAVE);
+#ifdef __mips64
+ __asm__ __volatile__ ("sd $28,%0" : "=m" (gpsave) : : );
+#endif
+ __asm__ ("move %0, $29" : "=r" (stackref) : );
+ {
+ SLP_SAVE_STATE(stackref, stsizediff);
+ __asm__ __volatile__ (
+#ifdef __mips64
+ "daddu $29, %0\n"
+#else
+ "addu $29, %0\n"
+#endif
+ : /* no outputs */
+ : "r" (stsizediff)
+ );
+ SLP_RESTORE_STATE();
+ }
+#ifdef __mips64
+ __asm__ __volatile__ ("ld $28,%0" : : "m" (gpsave) : );
+#endif
+ __asm__ __volatile__ ("" : : : REGS_TO_SAVE);
+ __asm__ __volatile__ ("move %0, $0" : "=r" (err));
+ return err;
+}
+
+#endif
+
+/*
+ * further self-processing support
+ */
+
+/*
+ * if you want to add self-inspection tools, place them
+ * here. See the x86_msvc for the necessary defines.
+ * These features are highly experimental und not
+ * essential yet.
+ */
diff --git a/venv/lib/python3.10/site-packages/greenlet/platform/switch_ppc64_aix.h b/venv/lib/python3.10/site-packages/greenlet/platform/switch_ppc64_aix.h
new file mode 100644
index 000000000..e7e0b8776
--- /dev/null
+++ b/venv/lib/python3.10/site-packages/greenlet/platform/switch_ppc64_aix.h
@@ -0,0 +1,103 @@
+/*
+ * this is the internal transfer function.
+ *
+ * HISTORY
+ * 16-Oct-20 Jesse Gorzinski
+ * Copied from Linux PPC64 implementation
+ * 04-Sep-18 Alexey Borzenkov
+ * Workaround a gcc bug using manual save/restore of r30
+ * 21-Mar-18 Tulio Magno Quites Machado Filho
+ * Added r30 to the list of saved registers in order to fully comply with
+ * both ppc64 ELFv1 ABI and the ppc64le ELFv2 ABI, that classify this
+ * register as a nonvolatile register used for local variables.
+ * 21-Mar-18 Laszlo Boszormenyi
+ * Save r2 (TOC pointer) manually.
+ * 10-Dec-13 Ulrich Weigand
+ * Support ELFv2 ABI. Save float/vector registers.
+ * 09-Mar-12 Michael Ellerman
+ * 64-bit implementation, copied from 32-bit.
+ * 07-Sep-05 (py-dev mailing list discussion)
+ * removed 'r31' from the register-saved. !!!! WARNING !!!!
+ * It means that this file can no longer be compiled statically!
+ * It is now only suitable as part of a dynamic library!
+ * 14-Jan-04 Bob Ippolito
+ * added cr2-cr4 to the registers to be saved.
+ * Open questions: Should we save FP registers?
+ * What about vector registers?
+ * Differences between darwin and unix?
+ * 24-Nov-02 Christian Tismer
+ * needed to add another magic constant to insure
+ * that f in slp_eval_frame(PyFrameObject *f)
+ * STACK_REFPLUS will probably be 1 in most cases.
+ * gets included into the saved stack area.
+ * 04-Oct-02 Gustavo Niemeyer
+ * Ported from MacOS version.
+ * 17-Sep-02 Christian Tismer
+ * after virtualizing stack save/restore, the
+ * stack size shrunk a bit. Needed to introduce
+ * an adjustment STACK_MAGIC per platform.
+ * 15-Sep-02 Gerd Woetzel