-
-{% endblock content %}
\ No newline at end of file
diff --git a/blog-app/instance/blogsite.db b/blog-app/instance/blogsite.db
deleted file mode 100644
index dadbd8e..0000000
Binary files a/blog-app/instance/blogsite.db and /dev/null differ
diff --git a/blog-app/requirements.txt b/blog-app/requirements.txt
deleted file mode 100644
index 3adfbe9..0000000
Binary files a/blog-app/requirements.txt and /dev/null differ
diff --git a/blog-app/run.py b/blog-app/run.py
deleted file mode 100644
index df9930f..0000000
--- a/blog-app/run.py
+++ /dev/null
@@ -1,5 +0,0 @@
-
-from blogapp import app
-
-if __name__ == '__main__':
- app.run(debug=True, host="0.0.0.0", port=8700)
diff --git a/blog-app/venv/Include/site/python3.10/greenlet/greenlet.h b/blog-app/venv/Include/site/python3.10/greenlet/greenlet.h
new file mode 100644
index 0000000..d02a16e
--- /dev/null
+++ b/blog-app/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/blog-app/venv/Lib/site-packages/__pycache__/typing_extensions.cpython-310.pyc b/blog-app/venv/Lib/site-packages/__pycache__/typing_extensions.cpython-310.pyc
new file mode 100644
index 0000000..c256bcd
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/__pycache__/typing_extensions.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/_distutils_hack/__init__.py b/blog-app/venv/Lib/site-packages/_distutils_hack/__init__.py
new file mode 100644
index 0000000..f987a53
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/_distutils_hack/__init__.py
@@ -0,0 +1,222 @@
+# don't import any costly modules
+import sys
+import os
+
+
+is_pypy = '__pypy__' in sys.builtin_module_names
+
+
+def warn_distutils_present():
+ if 'distutils' not in sys.modules:
+ return
+ if is_pypy and sys.version_info < (3, 7):
+ # PyPy for 3.6 unconditionally imports distutils, so bypass the warning
+ # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
+ return
+ import warnings
+
+ warnings.warn(
+ "Distutils was imported before Setuptools, but importing Setuptools "
+ "also replaces the `distutils` module in `sys.modules`. This may lead "
+ "to undesirable behaviors or errors. To avoid these issues, avoid "
+ "using distutils directly, ensure that setuptools is installed in the "
+ "traditional way (e.g. not an editable install), and/or make sure "
+ "that setuptools is always imported before distutils."
+ )
+
+
+def clear_distutils():
+ if 'distutils' not in sys.modules:
+ return
+ import warnings
+
+ warnings.warn("Setuptools is replacing distutils.")
+ mods = [
+ name
+ for name in sys.modules
+ if name == "distutils" or name.startswith("distutils.")
+ ]
+ for name in mods:
+ del sys.modules[name]
+
+
+def enabled():
+ """
+ Allow selection of distutils by environment variable.
+ """
+ which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local')
+ return which == 'local'
+
+
+def ensure_local_distutils():
+ import importlib
+
+ clear_distutils()
+
+ # With the DistutilsMetaFinder in place,
+ # perform an import to cause distutils to be
+ # loaded from setuptools._distutils. Ref #2906.
+ with shim():
+ importlib.import_module('distutils')
+
+ # check that submodules load as expected
+ core = importlib.import_module('distutils.core')
+ assert '_distutils' in core.__file__, core.__file__
+ assert 'setuptools._distutils.log' not in sys.modules
+
+
+def do_override():
+ """
+ Ensure that the local copy of distutils is preferred over stdlib.
+
+ See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
+ for more motivation.
+ """
+ if enabled():
+ warn_distutils_present()
+ ensure_local_distutils()
+
+
+class _TrivialRe:
+ def __init__(self, *patterns):
+ self._patterns = patterns
+
+ def match(self, string):
+ return all(pat in string for pat in self._patterns)
+
+
+class DistutilsMetaFinder:
+ def find_spec(self, fullname, path, target=None):
+ # optimization: only consider top level modules and those
+ # found in the CPython test suite.
+ if path is not None and not fullname.startswith('test.'):
+ return
+
+ method_name = 'spec_for_{fullname}'.format(**locals())
+ method = getattr(self, method_name, lambda: None)
+ return method()
+
+ def spec_for_distutils(self):
+ if self.is_cpython():
+ return
+
+ import importlib
+ import importlib.abc
+ import importlib.util
+
+ try:
+ mod = importlib.import_module('setuptools._distutils')
+ except Exception:
+ # There are a couple of cases where setuptools._distutils
+ # may not be present:
+ # - An older Setuptools without a local distutils is
+ # taking precedence. Ref #2957.
+ # - Path manipulation during sitecustomize removes
+ # setuptools from the path but only after the hook
+ # has been loaded. Ref #2980.
+ # In either case, fall back to stdlib behavior.
+ return
+
+ class DistutilsLoader(importlib.abc.Loader):
+ def create_module(self, spec):
+ mod.__name__ = 'distutils'
+ return mod
+
+ def exec_module(self, module):
+ pass
+
+ return importlib.util.spec_from_loader(
+ 'distutils', DistutilsLoader(), origin=mod.__file__
+ )
+
+ @staticmethod
+ def is_cpython():
+ """
+ Suppress supplying distutils for CPython (build and tests).
+ Ref #2965 and #3007.
+ """
+ return os.path.isfile('pybuilddir.txt')
+
+ def spec_for_pip(self):
+ """
+ Ensure stdlib distutils when running under pip.
+ See pypa/pip#8761 for rationale.
+ """
+ if self.pip_imported_during_build():
+ return
+ clear_distutils()
+ self.spec_for_distutils = lambda: None
+
+ @classmethod
+ def pip_imported_during_build(cls):
+ """
+ Detect if pip is being imported in a build script. Ref #2355.
+ """
+ import traceback
+
+ return any(
+ cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None)
+ )
+
+ @staticmethod
+ def frame_file_is_setup(frame):
+ """
+ Return True if the indicated frame suggests a setup.py file.
+ """
+ # some frames may not have __file__ (#2940)
+ return frame.f_globals.get('__file__', '').endswith('setup.py')
+
+ def spec_for_sensitive_tests(self):
+ """
+ Ensure stdlib distutils when running select tests under CPython.
+
+ python/cpython#91169
+ """
+ clear_distutils()
+ self.spec_for_distutils = lambda: None
+
+ sensitive_tests = (
+ [
+ 'test.test_distutils',
+ 'test.test_peg_generator',
+ 'test.test_importlib',
+ ]
+ if sys.version_info < (3, 10)
+ else [
+ 'test.test_distutils',
+ ]
+ )
+
+
+for name in DistutilsMetaFinder.sensitive_tests:
+ setattr(
+ DistutilsMetaFinder,
+ f'spec_for_{name}',
+ DistutilsMetaFinder.spec_for_sensitive_tests,
+ )
+
+
+DISTUTILS_FINDER = DistutilsMetaFinder()
+
+
+def add_shim():
+ DISTUTILS_FINDER in sys.meta_path or insert_shim()
+
+
+class shim:
+ def __enter__(self):
+ insert_shim()
+
+ def __exit__(self, exc, value, tb):
+ remove_shim()
+
+
+def insert_shim():
+ sys.meta_path.insert(0, DISTUTILS_FINDER)
+
+
+def remove_shim():
+ try:
+ sys.meta_path.remove(DISTUTILS_FINDER)
+ except ValueError:
+ pass
diff --git a/blog-app/venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc b/blog-app/venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..f1688e1
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/_distutils_hack/__pycache__/override.cpython-310.pyc b/blog-app/venv/Lib/site-packages/_distutils_hack/__pycache__/override.cpython-310.pyc
new file mode 100644
index 0000000..e308a48
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/_distutils_hack/__pycache__/override.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/_distutils_hack/override.py b/blog-app/venv/Lib/site-packages/_distutils_hack/override.py
new file mode 100644
index 0000000..2cc433a
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/_distutils_hack/override.py
@@ -0,0 +1 @@
+__import__('_distutils_hack').do_override()
diff --git a/blog-app/venv/Lib/site-packages/distutils-precedence.pth b/blog-app/venv/Lib/site-packages/distutils-precedence.pth
new file mode 100644
index 0000000..7f009fe
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/distutils-precedence.pth
@@ -0,0 +1 @@
+import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'local') == 'local'; enabled and __import__('_distutils_hack').add_shim();
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/INSTALLER b/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/LICENSE.rst b/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/LICENSE.rst
new file mode 100644
index 0000000..9d227a0
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/LICENSE.rst
@@ -0,0 +1,28 @@
+Copyright 2010 Pallets
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/METADATA b/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/METADATA
new file mode 100644
index 0000000..92f239c
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/METADATA
@@ -0,0 +1,109 @@
+Metadata-Version: 2.1
+Name: Flask-SQLAlchemy
+Version: 3.1.1
+Summary: Add SQLAlchemy support to your Flask application.
+Maintainer-email: Pallets
+Requires-Python: >=3.8
+Description-Content-Type: text/x-rst
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Requires-Dist: flask>=2.2.5
+Requires-Dist: sqlalchemy>=2.0.16
+Project-URL: Changes, https://flask-sqlalchemy.palletsprojects.com/changes/
+Project-URL: Chat, https://discord.gg/pallets
+Project-URL: Documentation, https://flask-sqlalchemy.palletsprojects.com
+Project-URL: Donate, https://palletsprojects.com/donate
+Project-URL: Issue Tracker, https://github.com/pallets-eco/flask-sqlalchemy/issues/
+Project-URL: Source Code, https://github.com/pallets-eco/flask-sqlalchemy/
+
+Flask-SQLAlchemy
+================
+
+Flask-SQLAlchemy is an extension for `Flask`_ that adds support for
+`SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy
+with Flask by providing useful defaults and extra helpers that make it
+easier to accomplish common tasks.
+
+.. _Flask: https://palletsprojects.com/p/flask/
+.. _SQLAlchemy: https://www.sqlalchemy.org
+
+
+Installing
+----------
+
+Install and update using `pip`_:
+
+.. code-block:: text
+
+ $ pip install -U Flask-SQLAlchemy
+
+.. _pip: https://pip.pypa.io/en/stable/getting-started/
+
+
+A Simple Example
+----------------
+
+.. code-block:: python
+
+ from flask import Flask
+ from flask_sqlalchemy import SQLAlchemy
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
+
+ app = Flask(__name__)
+ app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite"
+
+ class Base(DeclarativeBase):
+ pass
+
+ db = SQLAlchemy(app, model_class=Base)
+
+ class User(db.Model):
+ id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
+ username: Mapped[str] = mapped_column(db.String, unique=True, nullable=False)
+
+ with app.app_context():
+ db.create_all()
+
+ db.session.add(User(username="example"))
+ db.session.commit()
+
+ users = db.session.execute(db.select(User)).scalars()
+
+
+Contributing
+------------
+
+For guidance on setting up a development environment and how to make a
+contribution to Flask-SQLAlchemy, see the `contributing guidelines`_.
+
+.. _contributing guidelines: https://github.com/pallets-eco/flask-sqlalchemy/blob/main/CONTRIBUTING.rst
+
+
+Donate
+------
+
+The Pallets organization develops and supports Flask-SQLAlchemy and
+other popular packages. In order to grow the community of contributors
+and users, and allow the maintainers to devote more time to the
+projects, `please donate today`_.
+
+.. _please donate today: https://palletsprojects.com/donate
+
+
+Links
+-----
+
+- Documentation: https://flask-sqlalchemy.palletsprojects.com/
+- Changes: https://flask-sqlalchemy.palletsprojects.com/changes/
+- PyPI Releases: https://pypi.org/project/Flask-SQLAlchemy/
+- Source Code: https://github.com/pallets-eco/flask-sqlalchemy/
+- Issue Tracker: https://github.com/pallets-eco/flask-sqlalchemy/issues/
+- Website: https://palletsprojects.com/
+- Twitter: https://twitter.com/PalletsTeam
+- Chat: https://discord.gg/pallets
+
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/RECORD b/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/RECORD
new file mode 100644
index 0000000..5808b11
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/RECORD
@@ -0,0 +1,27 @@
+flask_sqlalchemy-3.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+flask_sqlalchemy-3.1.1.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475
+flask_sqlalchemy-3.1.1.dist-info/METADATA,sha256=lBxR1akBt7n9XBjIVTL2OV52OhCfFrb-Mqtoe0DCbR8,3432
+flask_sqlalchemy-3.1.1.dist-info/RECORD,,
+flask_sqlalchemy-3.1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+flask_sqlalchemy-3.1.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
+flask_sqlalchemy/__init__.py,sha256=he_w4qQQVS2Z1ms5GCTptDTXNOXBXw0n8zSuWCp8n6Y,653
+flask_sqlalchemy/__pycache__/__init__.cpython-310.pyc,,
+flask_sqlalchemy/__pycache__/cli.cpython-310.pyc,,
+flask_sqlalchemy/__pycache__/extension.cpython-310.pyc,,
+flask_sqlalchemy/__pycache__/model.cpython-310.pyc,,
+flask_sqlalchemy/__pycache__/pagination.cpython-310.pyc,,
+flask_sqlalchemy/__pycache__/query.cpython-310.pyc,,
+flask_sqlalchemy/__pycache__/record_queries.cpython-310.pyc,,
+flask_sqlalchemy/__pycache__/session.cpython-310.pyc,,
+flask_sqlalchemy/__pycache__/table.cpython-310.pyc,,
+flask_sqlalchemy/__pycache__/track_modifications.cpython-310.pyc,,
+flask_sqlalchemy/cli.py,sha256=pg3QDxP36GW2qnwe_CpPtkRhPchyVSGM6zlBNWuNCFE,484
+flask_sqlalchemy/extension.py,sha256=71tP_kNtb5VgZdafy_OH1sWdZOA6PaT7cJqX7tKgZ-k,38261
+flask_sqlalchemy/model.py,sha256=_mSisC2Eni0TgTyFWeN_O4LIexTeP_sVTdxh03yMK50,11461
+flask_sqlalchemy/pagination.py,sha256=JFpllrqkRkwacb8DAmQWaz9wsvQa0dypfSkhUDSC2ws,11119
+flask_sqlalchemy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+flask_sqlalchemy/query.py,sha256=Uls9qbmnpb9Vba43EDfsRP17eHJ0X4VG7SE22tH5R3g,3748
+flask_sqlalchemy/record_queries.py,sha256=ouS1ayj16h76LJprx13iYdoFZbm6m8OncrOgAVbG1Sk,3520
+flask_sqlalchemy/session.py,sha256=pBbtN8iDc8yuGVt0k18BvZHh2uEI7QPzZXO7eXrRi1g,3426
+flask_sqlalchemy/table.py,sha256=wAPOy8qwyAxpMwOIUJY4iMOultzz2W0D6xvBkQ7U2CE,859
+flask_sqlalchemy/track_modifications.py,sha256=yieyozj7IiVzwnAGZ-ZrgqrzjrUfG0kPrXBfW_hStSU,2755
diff --git a/blog-app/blogapp/static/main.css b/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/REQUESTED
similarity index 100%
rename from blog-app/blogapp/static/main.css
rename to blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/REQUESTED
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/WHEEL b/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/WHEEL
new file mode 100644
index 0000000..3b5e64b
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy-3.1.1.dist-info/WHEEL
@@ -0,0 +1,4 @@
+Wheel-Version: 1.0
+Generator: flit 3.9.0
+Root-Is-Purelib: true
+Tag: py3-none-any
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__init__.py b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__init__.py
new file mode 100644
index 0000000..c2fa059
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__init__.py
@@ -0,0 +1,26 @@
+from __future__ import annotations
+
+import typing as t
+
+from .extension import SQLAlchemy
+
+__all__ = [
+ "SQLAlchemy",
+]
+
+
+def __getattr__(name: str) -> t.Any:
+ if name == "__version__":
+ import importlib.metadata
+ import warnings
+
+ warnings.warn(
+ "The '__version__' attribute is deprecated and will be removed in"
+ " Flask-SQLAlchemy 3.2. Use feature detection or"
+ " 'importlib.metadata.version(\"flask-sqlalchemy\")' instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return importlib.metadata.version("flask-sqlalchemy")
+
+ raise AttributeError(name)
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/__init__.cpython-310.pyc b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..7079ed9
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/__init__.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/cli.cpython-310.pyc b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/cli.cpython-310.pyc
new file mode 100644
index 0000000..df76971
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/cli.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/extension.cpython-310.pyc b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/extension.cpython-310.pyc
new file mode 100644
index 0000000..4b8916f
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/extension.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/model.cpython-310.pyc b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/model.cpython-310.pyc
new file mode 100644
index 0000000..135f62d
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/model.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/pagination.cpython-310.pyc b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/pagination.cpython-310.pyc
new file mode 100644
index 0000000..2414e25
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/pagination.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/query.cpython-310.pyc b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/query.cpython-310.pyc
new file mode 100644
index 0000000..034b2ab
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/query.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/record_queries.cpython-310.pyc b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/record_queries.cpython-310.pyc
new file mode 100644
index 0000000..c947e9f
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/record_queries.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/session.cpython-310.pyc b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/session.cpython-310.pyc
new file mode 100644
index 0000000..e378917
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/session.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/table.cpython-310.pyc b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/table.cpython-310.pyc
new file mode 100644
index 0000000..b66624c
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/table.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/track_modifications.cpython-310.pyc b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/track_modifications.cpython-310.pyc
new file mode 100644
index 0000000..6c98df5
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/__pycache__/track_modifications.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/cli.py b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/cli.py
new file mode 100644
index 0000000..d7d7e4b
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/cli.py
@@ -0,0 +1,16 @@
+from __future__ import annotations
+
+import typing as t
+
+from flask import current_app
+
+
+def add_models_to_shell() -> dict[str, t.Any]:
+ """Registered with :meth:`~flask.Flask.shell_context_processor` if
+ ``add_models_to_shell`` is enabled. Adds the ``db`` instance and all model classes
+ to ``flask shell``.
+ """
+ db = current_app.extensions["sqlalchemy"]
+ out = {m.class_.__name__: m.class_ for m in db.Model._sa_registry.mappers}
+ out["db"] = db
+ return out
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/extension.py b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/extension.py
new file mode 100644
index 0000000..43e1b9a
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/extension.py
@@ -0,0 +1,1008 @@
+from __future__ import annotations
+
+import os
+import types
+import typing as t
+import warnings
+from weakref import WeakKeyDictionary
+
+import sqlalchemy as sa
+import sqlalchemy.event as sa_event
+import sqlalchemy.exc as sa_exc
+import sqlalchemy.orm as sa_orm
+from flask import abort
+from flask import current_app
+from flask import Flask
+from flask import has_app_context
+
+from .model import _QueryProperty
+from .model import BindMixin
+from .model import DefaultMeta
+from .model import DefaultMetaNoName
+from .model import Model
+from .model import NameMixin
+from .pagination import Pagination
+from .pagination import SelectPagination
+from .query import Query
+from .session import _app_ctx_id
+from .session import Session
+from .table import _Table
+
+_O = t.TypeVar("_O", bound=object) # Based on sqlalchemy.orm._typing.py
+
+
+# Type accepted for model_class argument
+_FSA_MCT = t.TypeVar(
+ "_FSA_MCT",
+ bound=t.Union[
+ t.Type[Model],
+ sa_orm.DeclarativeMeta,
+ t.Type[sa_orm.DeclarativeBase],
+ t.Type[sa_orm.DeclarativeBaseNoMeta],
+ ],
+)
+
+
+# Type returned by make_declarative_base
+class _FSAModel(Model):
+ metadata: sa.MetaData
+
+
+def _get_2x_declarative_bases(
+ model_class: _FSA_MCT,
+) -> list[t.Type[t.Union[sa_orm.DeclarativeBase, sa_orm.DeclarativeBaseNoMeta]]]:
+ return [
+ b
+ for b in model_class.__bases__
+ if issubclass(b, (sa_orm.DeclarativeBase, sa_orm.DeclarativeBaseNoMeta))
+ ]
+
+
+class SQLAlchemy:
+ """Integrates SQLAlchemy with Flask. This handles setting up one or more engines,
+ associating tables and models with specific engines, and cleaning up connections and
+ sessions after each request.
+
+ Only the engine configuration is specific to each application, other things like
+ the model, table, metadata, and session are shared for all applications using that
+ extension instance. Call :meth:`init_app` to configure the extension on an
+ application.
+
+ After creating the extension, create model classes by subclassing :attr:`Model`, and
+ table classes with :attr:`Table`. These can be accessed before :meth:`init_app` is
+ called, making it possible to define the models separately from the application.
+
+ Accessing :attr:`session` and :attr:`engine` requires an active Flask application
+ context. This includes methods like :meth:`create_all` which use the engine.
+
+ This class also provides access to names in SQLAlchemy's ``sqlalchemy`` and
+ ``sqlalchemy.orm`` modules. For example, you can use ``db.Column`` and
+ ``db.relationship`` instead of importing ``sqlalchemy.Column`` and
+ ``sqlalchemy.orm.relationship``. This can be convenient when defining models.
+
+ :param app: Call :meth:`init_app` on this Flask application now.
+ :param metadata: Use this as the default :class:`sqlalchemy.schema.MetaData`. Useful
+ for setting a naming convention.
+ :param session_options: Arguments used by :attr:`session` to create each session
+ instance. A ``scopefunc`` key will be passed to the scoped session, not the
+ session instance. See :class:`sqlalchemy.orm.sessionmaker` for a list of
+ arguments.
+ :param query_class: Use this as the default query class for models and dynamic
+ relationships. The query interface is considered legacy in SQLAlchemy.
+ :param model_class: Use this as the model base class when creating the declarative
+ model class :attr:`Model`. Can also be a fully created declarative model class
+ for further customization.
+ :param engine_options: Default arguments used when creating every engine. These are
+ lower precedence than application config. See :func:`sqlalchemy.create_engine`
+ for a list of arguments.
+ :param add_models_to_shell: Add the ``db`` instance and all model classes to
+ ``flask shell``.
+
+ .. versionchanged:: 3.1.0
+ The ``metadata`` parameter can still be used with SQLAlchemy 1.x classes,
+ but is ignored when using SQLAlchemy 2.x style of declarative classes.
+ Instead, specify metadata on your Base class.
+
+ .. versionchanged:: 3.1.0
+ Added the ``disable_autonaming`` parameter.
+
+ .. versionchanged:: 3.1.0
+ Changed ``model_class`` parameter to accepta SQLAlchemy 2.x
+ declarative base subclass.
+
+ .. versionchanged:: 3.0
+ An active Flask application context is always required to access ``session`` and
+ ``engine``.
+
+ .. versionchanged:: 3.0
+ Separate ``metadata`` are used for each bind key.
+
+ .. versionchanged:: 3.0
+ The ``engine_options`` parameter is applied as defaults before per-engine
+ configuration.
+
+ .. versionchanged:: 3.0
+ The session class can be customized in ``session_options``.
+
+ .. versionchanged:: 3.0
+ Added the ``add_models_to_shell`` parameter.
+
+ .. versionchanged:: 3.0
+ Engines are created when calling ``init_app`` rather than the first time they
+ are accessed.
+
+ .. versionchanged:: 3.0
+ All parameters except ``app`` are keyword-only.
+
+ .. versionchanged:: 3.0
+ The extension instance is stored directly as ``app.extensions["sqlalchemy"]``.
+
+ .. versionchanged:: 3.0
+ Setup methods are renamed with a leading underscore. They are considered
+ internal interfaces which may change at any time.
+
+ .. versionchanged:: 3.0
+ Removed the ``use_native_unicode`` parameter and config.
+
+ .. versionchanged:: 2.4
+ Added the ``engine_options`` parameter.
+
+ .. versionchanged:: 2.1
+ Added the ``metadata``, ``query_class``, and ``model_class`` parameters.
+
+ .. versionchanged:: 2.1
+ Use the same query class across ``session``, ``Model.query`` and
+ ``Query``.
+
+ .. versionchanged:: 0.16
+ ``scopefunc`` is accepted in ``session_options``.
+
+ .. versionchanged:: 0.10
+ Added the ``session_options`` parameter.
+ """
+
+ def __init__(
+ self,
+ app: Flask | None = None,
+ *,
+ metadata: sa.MetaData | None = None,
+ session_options: dict[str, t.Any] | None = None,
+ query_class: type[Query] = Query,
+ model_class: _FSA_MCT = Model, # type: ignore[assignment]
+ engine_options: dict[str, t.Any] | None = None,
+ add_models_to_shell: bool = True,
+ disable_autonaming: bool = False,
+ ):
+ if session_options is None:
+ session_options = {}
+
+ self.Query = query_class
+ """The default query class used by ``Model.query`` and ``lazy="dynamic"``
+ relationships.
+
+ .. warning::
+ The query interface is considered legacy in SQLAlchemy.
+
+ Customize this by passing the ``query_class`` parameter to the extension.
+ """
+
+ self.session = self._make_scoped_session(session_options)
+ """A :class:`sqlalchemy.orm.scoping.scoped_session` that creates instances of
+ :class:`.Session` scoped to the current Flask application context. The session
+ will be removed, returning the engine connection to the pool, when the
+ application context exits.
+
+ Customize this by passing ``session_options`` to the extension.
+
+ This requires that a Flask application context is active.
+
+ .. versionchanged:: 3.0
+ The session is scoped to the current app context.
+ """
+
+ self.metadatas: dict[str | None, sa.MetaData] = {}
+ """Map of bind keys to :class:`sqlalchemy.schema.MetaData` instances. The
+ ``None`` key refers to the default metadata, and is available as
+ :attr:`metadata`.
+
+ Customize the default metadata by passing the ``metadata`` parameter to the
+ extension. This can be used to set a naming convention. When metadata for
+ another bind key is created, it copies the default's naming convention.
+
+ .. versionadded:: 3.0
+ """
+
+ if metadata is not None:
+ if len(_get_2x_declarative_bases(model_class)) > 0:
+ warnings.warn(
+ "When using SQLAlchemy 2.x style of declarative classes,"
+ " the `metadata` should be an attribute of the base class."
+ "The metadata passed into SQLAlchemy() is ignored.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ else:
+ metadata.info["bind_key"] = None
+ self.metadatas[None] = metadata
+
+ self.Table = self._make_table_class()
+ """A :class:`sqlalchemy.schema.Table` class that chooses a metadata
+ automatically.
+
+ Unlike the base ``Table``, the ``metadata`` argument is not required. If it is
+ not given, it is selected based on the ``bind_key`` argument.
+
+ :param bind_key: Used to select a different metadata.
+ :param args: Arguments passed to the base class. These are typically the table's
+ name, columns, and constraints.
+ :param kwargs: Arguments passed to the base class.
+
+ .. versionchanged:: 3.0
+ This is a subclass of SQLAlchemy's ``Table`` rather than a function.
+ """
+
+ self.Model = self._make_declarative_base(
+ model_class, disable_autonaming=disable_autonaming
+ )
+ """A SQLAlchemy declarative model class. Subclass this to define database
+ models.
+
+ If a model does not set ``__tablename__``, it will be generated by converting
+ the class name from ``CamelCase`` to ``snake_case``. It will not be generated
+ if the model looks like it uses single-table inheritance.
+
+ If a model or parent class sets ``__bind_key__``, it will use that metadata and
+ database engine. Otherwise, it will use the default :attr:`metadata` and
+ :attr:`engine`. This is ignored if the model sets ``metadata`` or ``__table__``.
+
+ For code using the SQLAlchemy 1.x API, customize this model by subclassing
+ :class:`.Model` and passing the ``model_class`` parameter to the extension.
+ A fully created declarative model class can be
+ passed as well, to use a custom metaclass.
+
+ For code using the SQLAlchemy 2.x API, customize this model by subclassing
+ :class:`sqlalchemy.orm.DeclarativeBase` or
+ :class:`sqlalchemy.orm.DeclarativeBaseNoMeta`
+ and passing the ``model_class`` parameter to the extension.
+ """
+
+ if engine_options is None:
+ engine_options = {}
+
+ self._engine_options = engine_options
+ self._app_engines: WeakKeyDictionary[Flask, dict[str | None, sa.engine.Engine]]
+ self._app_engines = WeakKeyDictionary()
+ self._add_models_to_shell = add_models_to_shell
+
+ if app is not None:
+ self.init_app(app)
+
+ def __repr__(self) -> str:
+ if not has_app_context():
+ return f"<{type(self).__name__}>"
+
+ message = f"{type(self).__name__} {self.engine.url}"
+
+ if len(self.engines) > 1:
+ message = f"{message} +{len(self.engines) - 1}"
+
+ return f"<{message}>"
+
+ def init_app(self, app: Flask) -> None:
+ """Initialize a Flask application for use with this extension instance. This
+ must be called before accessing the database engine or session with the app.
+
+ This sets default configuration values, then configures the extension on the
+ application and creates the engines for each bind key. Therefore, this must be
+ called after the application has been configured. Changes to application config
+ after this call will not be reflected.
+
+ The following keys from ``app.config`` are used:
+
+ - :data:`.SQLALCHEMY_DATABASE_URI`
+ - :data:`.SQLALCHEMY_ENGINE_OPTIONS`
+ - :data:`.SQLALCHEMY_ECHO`
+ - :data:`.SQLALCHEMY_BINDS`
+ - :data:`.SQLALCHEMY_RECORD_QUERIES`
+ - :data:`.SQLALCHEMY_TRACK_MODIFICATIONS`
+
+ :param app: The Flask application to initialize.
+ """
+ if "sqlalchemy" in app.extensions:
+ raise RuntimeError(
+ "A 'SQLAlchemy' instance has already been registered on this Flask app."
+ " Import and use that instance instead."
+ )
+
+ app.extensions["sqlalchemy"] = self
+ app.teardown_appcontext(self._teardown_session)
+
+ if self._add_models_to_shell:
+ from .cli import add_models_to_shell
+
+ app.shell_context_processor(add_models_to_shell)
+
+ basic_uri: str | sa.engine.URL | None = app.config.setdefault(
+ "SQLALCHEMY_DATABASE_URI", None
+ )
+ basic_engine_options = self._engine_options.copy()
+ basic_engine_options.update(
+ app.config.setdefault("SQLALCHEMY_ENGINE_OPTIONS", {})
+ )
+ echo: bool = app.config.setdefault("SQLALCHEMY_ECHO", False)
+ config_binds: dict[
+ str | None, str | sa.engine.URL | dict[str, t.Any]
+ ] = app.config.setdefault("SQLALCHEMY_BINDS", {})
+ engine_options: dict[str | None, dict[str, t.Any]] = {}
+
+ # Build the engine config for each bind key.
+ for key, value in config_binds.items():
+ engine_options[key] = self._engine_options.copy()
+
+ if isinstance(value, (str, sa.engine.URL)):
+ engine_options[key]["url"] = value
+ else:
+ engine_options[key].update(value)
+
+ # Build the engine config for the default bind key.
+ if basic_uri is not None:
+ basic_engine_options["url"] = basic_uri
+
+ if "url" in basic_engine_options:
+ engine_options.setdefault(None, {}).update(basic_engine_options)
+
+ if not engine_options:
+ raise RuntimeError(
+ "Either 'SQLALCHEMY_DATABASE_URI' or 'SQLALCHEMY_BINDS' must be set."
+ )
+
+ engines = self._app_engines.setdefault(app, {})
+
+ # Dispose existing engines in case init_app is called again.
+ if engines:
+ for engine in engines.values():
+ engine.dispose()
+
+ engines.clear()
+
+ # Create the metadata and engine for each bind key.
+ for key, options in engine_options.items():
+ self._make_metadata(key)
+ options.setdefault("echo", echo)
+ options.setdefault("echo_pool", echo)
+ self._apply_driver_defaults(options, app)
+ engines[key] = self._make_engine(key, options, app)
+
+ if app.config.setdefault("SQLALCHEMY_RECORD_QUERIES", False):
+ from . import record_queries
+
+ for engine in engines.values():
+ record_queries._listen(engine)
+
+ if app.config.setdefault("SQLALCHEMY_TRACK_MODIFICATIONS", False):
+ from . import track_modifications
+
+ track_modifications._listen(self.session)
+
+ def _make_scoped_session(
+ self, options: dict[str, t.Any]
+ ) -> sa_orm.scoped_session[Session]:
+ """Create a :class:`sqlalchemy.orm.scoping.scoped_session` around the factory
+ from :meth:`_make_session_factory`. The result is available as :attr:`session`.
+
+ The scope function can be customized using the ``scopefunc`` key in the
+ ``session_options`` parameter to the extension. By default it uses the current
+ thread or greenlet id.
+
+ This method is used for internal setup. Its signature may change at any time.
+
+ :meta private:
+
+ :param options: The ``session_options`` parameter from ``__init__``. Keyword
+ arguments passed to the session factory. A ``scopefunc`` key is popped.
+
+ .. versionchanged:: 3.0
+ The session is scoped to the current app context.
+
+ .. versionchanged:: 3.0
+ Renamed from ``create_scoped_session``, this method is internal.
+ """
+ scope = options.pop("scopefunc", _app_ctx_id)
+ factory = self._make_session_factory(options)
+ return sa_orm.scoped_session(factory, scope)
+
+ def _make_session_factory(
+ self, options: dict[str, t.Any]
+ ) -> sa_orm.sessionmaker[Session]:
+ """Create the SQLAlchemy :class:`sqlalchemy.orm.sessionmaker` used by
+ :meth:`_make_scoped_session`.
+
+ To customize, pass the ``session_options`` parameter to :class:`SQLAlchemy`. To
+ customize the session class, subclass :class:`.Session` and pass it as the
+ ``class_`` key.
+
+ This method is used for internal setup. Its signature may change at any time.
+
+ :meta private:
+
+ :param options: The ``session_options`` parameter from ``__init__``. Keyword
+ arguments passed to the session factory.
+
+ .. versionchanged:: 3.0
+ The session class can be customized.
+
+ .. versionchanged:: 3.0
+ Renamed from ``create_session``, this method is internal.
+ """
+ options.setdefault("class_", Session)
+ options.setdefault("query_cls", self.Query)
+ return sa_orm.sessionmaker(db=self, **options)
+
+ def _teardown_session(self, exc: BaseException | None) -> None:
+ """Remove the current session at the end of the request.
+
+ :meta private:
+
+ .. versionadded:: 3.0
+ """
+ self.session.remove()
+
+ def _make_metadata(self, bind_key: str | None) -> sa.MetaData:
+ """Get or create a :class:`sqlalchemy.schema.MetaData` for the given bind key.
+
+ This method is used for internal setup. Its signature may change at any time.
+
+ :meta private:
+
+ :param bind_key: The name of the metadata being created.
+
+ .. versionadded:: 3.0
+ """
+ if bind_key in self.metadatas:
+ return self.metadatas[bind_key]
+
+ if bind_key is not None:
+ # Copy the naming convention from the default metadata.
+ naming_convention = self._make_metadata(None).naming_convention
+ else:
+ naming_convention = None
+
+ # Set the bind key in info to be used by session.get_bind.
+ metadata = sa.MetaData(
+ naming_convention=naming_convention, info={"bind_key": bind_key}
+ )
+ self.metadatas[bind_key] = metadata
+ return metadata
+
+ def _make_table_class(self) -> type[_Table]:
+ """Create a SQLAlchemy :class:`sqlalchemy.schema.Table` class that chooses a
+ metadata automatically based on the ``bind_key``. The result is available as
+ :attr:`Table`.
+
+ This method is used for internal setup. Its signature may change at any time.
+
+ :meta private:
+
+ .. versionadded:: 3.0
+ """
+
+ class Table(_Table):
+ def __new__(
+ cls, *args: t.Any, bind_key: str | None = None, **kwargs: t.Any
+ ) -> Table:
+ # If a metadata arg is passed, go directly to the base Table. Also do
+ # this for no args so the correct error is shown.
+ if not args or (len(args) >= 2 and isinstance(args[1], sa.MetaData)):
+ return super().__new__(cls, *args, **kwargs)
+
+ metadata = self._make_metadata(bind_key)
+ return super().__new__(cls, *[args[0], metadata, *args[1:]], **kwargs)
+
+ return Table
+
+ def _make_declarative_base(
+ self,
+ model_class: _FSA_MCT,
+ disable_autonaming: bool = False,
+ ) -> t.Type[_FSAModel]:
+ """Create a SQLAlchemy declarative model class. The result is available as
+ :attr:`Model`.
+
+ To customize, subclass :class:`.Model` and pass it as ``model_class`` to
+ :class:`SQLAlchemy`. To customize at the metaclass level, pass an already
+ created declarative model class as ``model_class``.
+
+ This method is used for internal setup. Its signature may change at any time.
+
+ :meta private:
+
+ :param model_class: A model base class, or an already created declarative model
+ class.
+
+ :param disable_autonaming: Turns off automatic tablename generation in models.
+
+ .. versionchanged:: 3.1.0
+ Added support for passing SQLAlchemy 2.x base class as model class.
+ Added optional ``disable_autonaming`` parameter.
+
+ .. versionchanged:: 3.0
+ Renamed with a leading underscore, this method is internal.
+
+ .. versionchanged:: 2.3
+ ``model`` can be an already created declarative model class.
+ """
+ model: t.Type[_FSAModel]
+ declarative_bases = _get_2x_declarative_bases(model_class)
+ if len(declarative_bases) > 1:
+ # raise error if more than one declarative base is found
+ raise ValueError(
+ "Only one declarative base can be passed to SQLAlchemy."
+ " Got: {}".format(model_class.__bases__)
+ )
+ elif len(declarative_bases) == 1:
+ body = dict(model_class.__dict__)
+ body["__fsa__"] = self
+ mixin_classes = [BindMixin, NameMixin, Model]
+ if disable_autonaming:
+ mixin_classes.remove(NameMixin)
+ model = types.new_class(
+ "FlaskSQLAlchemyBase",
+ (*mixin_classes, *model_class.__bases__),
+ {"metaclass": type(declarative_bases[0])},
+ lambda ns: ns.update(body),
+ )
+ elif not isinstance(model_class, sa_orm.DeclarativeMeta):
+ metadata = self._make_metadata(None)
+ metaclass = DefaultMetaNoName if disable_autonaming else DefaultMeta
+ model = sa_orm.declarative_base(
+ metadata=metadata, cls=model_class, name="Model", metaclass=metaclass
+ )
+ else:
+ model = model_class # type: ignore[assignment]
+
+ if None not in self.metadatas:
+ # Use the model's metadata as the default metadata.
+ model.metadata.info["bind_key"] = None
+ self.metadatas[None] = model.metadata
+ else:
+ # Use the passed in default metadata as the model's metadata.
+ model.metadata = self.metadatas[None]
+
+ model.query_class = self.Query
+ model.query = _QueryProperty() # type: ignore[assignment]
+ model.__fsa__ = self
+ return model
+
+ def _apply_driver_defaults(self, options: dict[str, t.Any], app: Flask) -> None:
+ """Apply driver-specific configuration to an engine.
+
+ SQLite in-memory databases use ``StaticPool`` and disable ``check_same_thread``.
+ File paths are relative to the app's :attr:`~flask.Flask.instance_path`,
+ which is created if it doesn't exist.
+
+ MySQL sets ``charset="utf8mb4"``, and ``pool_timeout`` defaults to 2 hours.
+
+ This method is used for internal setup. Its signature may change at any time.
+
+ :meta private:
+
+ :param options: Arguments passed to the engine.
+ :param app: The application that the engine configuration belongs to.
+
+ .. versionchanged:: 3.0
+ SQLite paths are relative to ``app.instance_path``. It does not use
+ ``NullPool`` if ``pool_size`` is 0. Driver-level URIs are supported.
+
+ .. versionchanged:: 3.0
+ MySQL sets ``charset="utf8mb4". It does not set ``pool_size`` to 10. It
+ does not set ``pool_recycle`` if not using a queue pool.
+
+ .. versionchanged:: 3.0
+ Renamed from ``apply_driver_hacks``, this method is internal. It does not
+ return anything.
+
+ .. versionchanged:: 2.5
+ Returns ``(sa_url, options)``.
+ """
+ url = sa.engine.make_url(options["url"])
+
+ if url.drivername in {"sqlite", "sqlite+pysqlite"}:
+ if url.database is None or url.database in {"", ":memory:"}:
+ options["poolclass"] = sa.pool.StaticPool
+
+ if "connect_args" not in options:
+ options["connect_args"] = {}
+
+ options["connect_args"]["check_same_thread"] = False
+ else:
+ # the url might look like sqlite:///file:path?uri=true
+ is_uri = url.query.get("uri", False)
+
+ if is_uri:
+ db_str = url.database[5:]
+ else:
+ db_str = url.database
+
+ if not os.path.isabs(db_str):
+ os.makedirs(app.instance_path, exist_ok=True)
+ db_str = os.path.join(app.instance_path, db_str)
+
+ if is_uri:
+ db_str = f"file:{db_str}"
+
+ options["url"] = url.set(database=db_str)
+ elif url.drivername.startswith("mysql"):
+ # set queue defaults only when using queue pool
+ if (
+ "pool_class" not in options
+ or options["pool_class"] is sa.pool.QueuePool
+ ):
+ options.setdefault("pool_recycle", 7200)
+
+ if "charset" not in url.query:
+ options["url"] = url.update_query_dict({"charset": "utf8mb4"})
+
+ def _make_engine(
+ self, bind_key: str | None, options: dict[str, t.Any], app: Flask
+ ) -> sa.engine.Engine:
+ """Create the :class:`sqlalchemy.engine.Engine` for the given bind key and app.
+
+ To customize, use :data:`.SQLALCHEMY_ENGINE_OPTIONS` or
+ :data:`.SQLALCHEMY_BINDS` config. Pass ``engine_options`` to :class:`SQLAlchemy`
+ to set defaults for all engines.
+
+ This method is used for internal setup. Its signature may change at any time.
+
+ :meta private:
+
+ :param bind_key: The name of the engine being created.
+ :param options: Arguments passed to the engine.
+ :param app: The application that the engine configuration belongs to.
+
+ .. versionchanged:: 3.0
+ Renamed from ``create_engine``, this method is internal.
+ """
+ return sa.engine_from_config(options, prefix="")
+
+ @property
+ def metadata(self) -> sa.MetaData:
+ """The default metadata used by :attr:`Model` and :attr:`Table` if no bind key
+ is set.
+ """
+ return self.metadatas[None]
+
+ @property
+ def engines(self) -> t.Mapping[str | None, sa.engine.Engine]:
+ """Map of bind keys to :class:`sqlalchemy.engine.Engine` instances for current
+ application. The ``None`` key refers to the default engine, and is available as
+ :attr:`engine`.
+
+ To customize, set the :data:`.SQLALCHEMY_BINDS` config, and set defaults by
+ passing the ``engine_options`` parameter to the extension.
+
+ This requires that a Flask application context is active.
+
+ .. versionadded:: 3.0
+ """
+ app = current_app._get_current_object() # type: ignore[attr-defined]
+
+ if app not in self._app_engines:
+ raise RuntimeError(
+ "The current Flask app is not registered with this 'SQLAlchemy'"
+ " instance. Did you forget to call 'init_app', or did you create"
+ " multiple 'SQLAlchemy' instances?"
+ )
+
+ return self._app_engines[app]
+
+ @property
+ def engine(self) -> sa.engine.Engine:
+ """The default :class:`~sqlalchemy.engine.Engine` for the current application,
+ used by :attr:`session` if the :attr:`Model` or :attr:`Table` being queried does
+ not set a bind key.
+
+ To customize, set the :data:`.SQLALCHEMY_ENGINE_OPTIONS` config, and set
+ defaults by passing the ``engine_options`` parameter to the extension.
+
+ This requires that a Flask application context is active.
+ """
+ return self.engines[None]
+
+ def get_engine(
+ self, bind_key: str | None = None, **kwargs: t.Any
+ ) -> sa.engine.Engine:
+ """Get the engine for the given bind key for the current application.
+ This requires that a Flask application context is active.
+
+ :param bind_key: The name of the engine.
+
+ .. deprecated:: 3.0
+ Will be removed in Flask-SQLAlchemy 3.2. Use ``engines[key]`` instead.
+
+ .. versionchanged:: 3.0
+ Renamed the ``bind`` parameter to ``bind_key``. Removed the ``app``
+ parameter.
+ """
+ warnings.warn(
+ "'get_engine' is deprecated and will be removed in Flask-SQLAlchemy"
+ " 3.2. Use 'engine' or 'engines[key]' instead. If you're using"
+ " Flask-Migrate or Alembic, you'll need to update your 'env.py' file.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+
+ if "bind" in kwargs:
+ bind_key = kwargs.pop("bind")
+
+ return self.engines[bind_key]
+
+ def get_or_404(
+ self,
+ entity: type[_O],
+ ident: t.Any,
+ *,
+ description: str | None = None,
+ **kwargs: t.Any,
+ ) -> _O:
+ """Like :meth:`session.get() ` but aborts with a
+ ``404 Not Found`` error instead of returning ``None``.
+
+ :param entity: The model class to query.
+ :param ident: The primary key to query.
+ :param description: A custom message to show on the error page.
+ :param kwargs: Extra arguments passed to ``session.get()``.
+
+ .. versionchanged:: 3.1
+ Pass extra keyword arguments to ``session.get()``.
+
+ .. versionadded:: 3.0
+ """
+ value = self.session.get(entity, ident, **kwargs)
+
+ if value is None:
+ abort(404, description=description)
+
+ return value
+
+ def first_or_404(
+ self, statement: sa.sql.Select[t.Any], *, description: str | None = None
+ ) -> t.Any:
+ """Like :meth:`Result.scalar() `, but aborts
+ with a ``404 Not Found`` error instead of returning ``None``.
+
+ :param statement: The ``select`` statement to execute.
+ :param description: A custom message to show on the error page.
+
+ .. versionadded:: 3.0
+ """
+ value = self.session.execute(statement).scalar()
+
+ if value is None:
+ abort(404, description=description)
+
+ return value
+
+ def one_or_404(
+ self, statement: sa.sql.Select[t.Any], *, description: str | None = None
+ ) -> t.Any:
+ """Like :meth:`Result.scalar_one() `,
+ but aborts with a ``404 Not Found`` error instead of raising ``NoResultFound``
+ or ``MultipleResultsFound``.
+
+ :param statement: The ``select`` statement to execute.
+ :param description: A custom message to show on the error page.
+
+ .. versionadded:: 3.0
+ """
+ try:
+ return self.session.execute(statement).scalar_one()
+ except (sa_exc.NoResultFound, sa_exc.MultipleResultsFound):
+ abort(404, description=description)
+
+ def paginate(
+ self,
+ select: sa.sql.Select[t.Any],
+ *,
+ page: int | None = None,
+ per_page: int | None = None,
+ max_per_page: int | None = None,
+ error_out: bool = True,
+ count: bool = True,
+ ) -> Pagination:
+ """Apply an offset and limit to a select statment based on the current page and
+ number of items per page, returning a :class:`.Pagination` object.
+
+ The statement should select a model class, like ``select(User)``. This applies
+ ``unique()`` and ``scalars()`` modifiers to the result, so compound selects will
+ not return the expected results.
+
+ :param select: The ``select`` statement to paginate.
+ :param page: The current page, used to calculate the offset. Defaults to the
+ ``page`` query arg during a request, or 1 otherwise.
+ :param per_page: The maximum number of items on a page, used to calculate the
+ offset and limit. Defaults to the ``per_page`` query arg during a request,
+ or 20 otherwise.
+ :param max_per_page: The maximum allowed value for ``per_page``, to limit a
+ user-provided value. Use ``None`` for no limit. Defaults to 100.
+ :param error_out: Abort with a ``404 Not Found`` error if no items are returned
+ and ``page`` is not 1, or if ``page`` or ``per_page`` is less than 1, or if
+ either are not ints.
+ :param count: Calculate the total number of values by issuing an extra count
+ query. For very complex queries this may be inaccurate or slow, so it can be
+ disabled and set manually if necessary.
+
+ .. versionchanged:: 3.0
+ The ``count`` query is more efficient.
+
+ .. versionadded:: 3.0
+ """
+ return SelectPagination(
+ select=select,
+ session=self.session(),
+ page=page,
+ per_page=per_page,
+ max_per_page=max_per_page,
+ error_out=error_out,
+ count=count,
+ )
+
+ def _call_for_binds(
+ self, bind_key: str | None | list[str | None], op_name: str
+ ) -> None:
+ """Call a method on each metadata.
+
+ :meta private:
+
+ :param bind_key: A bind key or list of keys. Defaults to all binds.
+ :param op_name: The name of the method to call.
+
+ .. versionchanged:: 3.0
+ Renamed from ``_execute_for_all_tables``.
+ """
+ if bind_key == "__all__":
+ keys: list[str | None] = list(self.metadatas)
+ elif bind_key is None or isinstance(bind_key, str):
+ keys = [bind_key]
+ else:
+ keys = bind_key
+
+ for key in keys:
+ try:
+ engine = self.engines[key]
+ except KeyError:
+ message = f"Bind key '{key}' is not in 'SQLALCHEMY_BINDS' config."
+
+ if key is None:
+ message = f"'SQLALCHEMY_DATABASE_URI' config is not set. {message}"
+
+ raise sa_exc.UnboundExecutionError(message) from None
+
+ metadata = self.metadatas[key]
+ getattr(metadata, op_name)(bind=engine)
+
+ def create_all(self, bind_key: str | None | list[str | None] = "__all__") -> None:
+ """Create tables that do not exist in the database by calling
+ ``metadata.create_all()`` for all or some bind keys. This does not
+ update existing tables, use a migration library for that.
+
+ This requires that a Flask application context is active.
+
+ :param bind_key: A bind key or list of keys to create the tables for. Defaults
+ to all binds.
+
+ .. versionchanged:: 3.0
+ Renamed the ``bind`` parameter to ``bind_key``. Removed the ``app``
+ parameter.
+
+ .. versionchanged:: 0.12
+ Added the ``bind`` and ``app`` parameters.
+ """
+ self._call_for_binds(bind_key, "create_all")
+
+ def drop_all(self, bind_key: str | None | list[str | None] = "__all__") -> None:
+ """Drop tables by calling ``metadata.drop_all()`` for all or some bind keys.
+
+ This requires that a Flask application context is active.
+
+ :param bind_key: A bind key or list of keys to drop the tables from. Defaults to
+ all binds.
+
+ .. versionchanged:: 3.0
+ Renamed the ``bind`` parameter to ``bind_key``. Removed the ``app``
+ parameter.
+
+ .. versionchanged:: 0.12
+ Added the ``bind`` and ``app`` parameters.
+ """
+ self._call_for_binds(bind_key, "drop_all")
+
+ def reflect(self, bind_key: str | None | list[str | None] = "__all__") -> None:
+ """Load table definitions from the database by calling ``metadata.reflect()``
+ for all or some bind keys.
+
+ This requires that a Flask application context is active.
+
+ :param bind_key: A bind key or list of keys to reflect the tables from. Defaults
+ to all binds.
+
+ .. versionchanged:: 3.0
+ Renamed the ``bind`` parameter to ``bind_key``. Removed the ``app``
+ parameter.
+
+ .. versionchanged:: 0.12
+ Added the ``bind`` and ``app`` parameters.
+ """
+ self._call_for_binds(bind_key, "reflect")
+
+ def _set_rel_query(self, kwargs: dict[str, t.Any]) -> None:
+ """Apply the extension's :attr:`Query` class as the default for relationships
+ and backrefs.
+
+ :meta private:
+ """
+ kwargs.setdefault("query_class", self.Query)
+
+ if "backref" in kwargs:
+ backref = kwargs["backref"]
+
+ if isinstance(backref, str):
+ backref = (backref, {})
+
+ backref[1].setdefault("query_class", self.Query)
+
+ def relationship(
+ self, *args: t.Any, **kwargs: t.Any
+ ) -> sa_orm.RelationshipProperty[t.Any]:
+ """A :func:`sqlalchemy.orm.relationship` that applies this extension's
+ :attr:`Query` class for dynamic relationships and backrefs.
+
+ .. versionchanged:: 3.0
+ The :attr:`Query` class is set on ``backref``.
+ """
+ self._set_rel_query(kwargs)
+ return sa_orm.relationship(*args, **kwargs)
+
+ def dynamic_loader(
+ self, argument: t.Any, **kwargs: t.Any
+ ) -> sa_orm.RelationshipProperty[t.Any]:
+ """A :func:`sqlalchemy.orm.dynamic_loader` that applies this extension's
+ :attr:`Query` class for relationships and backrefs.
+
+ .. versionchanged:: 3.0
+ The :attr:`Query` class is set on ``backref``.
+ """
+ self._set_rel_query(kwargs)
+ return sa_orm.dynamic_loader(argument, **kwargs)
+
+ def _relation(
+ self, *args: t.Any, **kwargs: t.Any
+ ) -> sa_orm.RelationshipProperty[t.Any]:
+ """A :func:`sqlalchemy.orm.relationship` that applies this extension's
+ :attr:`Query` class for dynamic relationships and backrefs.
+
+ SQLAlchemy 2.0 removes this name, use ``relationship`` instead.
+
+ :meta private:
+
+ .. versionchanged:: 3.0
+ The :attr:`Query` class is set on ``backref``.
+ """
+ self._set_rel_query(kwargs)
+ f = sa_orm.relationship
+ return f(*args, **kwargs)
+
+ def __getattr__(self, name: str) -> t.Any:
+ if name == "relation":
+ return self._relation
+
+ if name == "event":
+ return sa_event
+
+ if name.startswith("_"):
+ raise AttributeError(name)
+
+ for mod in (sa, sa_orm):
+ if hasattr(mod, name):
+ return getattr(mod, name)
+
+ raise AttributeError(name)
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/model.py b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/model.py
new file mode 100644
index 0000000..c6f9e5a
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/model.py
@@ -0,0 +1,330 @@
+from __future__ import annotations
+
+import re
+import typing as t
+
+import sqlalchemy as sa
+import sqlalchemy.orm as sa_orm
+
+from .query import Query
+
+if t.TYPE_CHECKING:
+ from .extension import SQLAlchemy
+
+
+class _QueryProperty:
+ """A class property that creates a query object for a model.
+
+ :meta private:
+ """
+
+ def __get__(self, obj: Model | None, cls: type[Model]) -> Query:
+ return cls.query_class(
+ cls, session=cls.__fsa__.session() # type: ignore[arg-type]
+ )
+
+
+class Model:
+ """The base class of the :attr:`.SQLAlchemy.Model` declarative model class.
+
+ To define models, subclass :attr:`db.Model <.SQLAlchemy.Model>`, not this. To
+ customize ``db.Model``, subclass this and pass it as ``model_class`` to
+ :class:`.SQLAlchemy`. To customize ``db.Model`` at the metaclass level, pass an
+ already created declarative model class as ``model_class``.
+ """
+
+ __fsa__: t.ClassVar[SQLAlchemy]
+ """Internal reference to the extension object.
+
+ :meta private:
+ """
+
+ query_class: t.ClassVar[type[Query]] = Query
+ """Query class used by :attr:`query`. Defaults to :attr:`.SQLAlchemy.Query`, which
+ defaults to :class:`.Query`.
+ """
+
+ query: t.ClassVar[Query] = _QueryProperty() # type: ignore[assignment]
+ """A SQLAlchemy query for a model. Equivalent to ``db.session.query(Model)``. Can be
+ customized per-model by overriding :attr:`query_class`.
+
+ .. warning::
+ The query interface is considered legacy in SQLAlchemy. Prefer using
+ ``session.execute(select())`` instead.
+ """
+
+ def __repr__(self) -> str:
+ state = sa.inspect(self)
+ assert state is not None
+
+ if state.transient:
+ pk = f"(transient {id(self)})"
+ elif state.pending:
+ pk = f"(pending {id(self)})"
+ else:
+ pk = ", ".join(map(str, state.identity))
+
+ return f"<{type(self).__name__} {pk}>"
+
+
+class BindMetaMixin(type):
+ """Metaclass mixin that sets a model's ``metadata`` based on its ``__bind_key__``.
+
+ If the model sets ``metadata`` or ``__table__`` directly, ``__bind_key__`` is
+ ignored. If the ``metadata`` is the same as the parent model, it will not be set
+ directly on the child model.
+ """
+
+ __fsa__: SQLAlchemy
+ metadata: sa.MetaData
+
+ def __init__(
+ cls, name: str, bases: tuple[type, ...], d: dict[str, t.Any], **kwargs: t.Any
+ ) -> None:
+ if not ("metadata" in cls.__dict__ or "__table__" in cls.__dict__):
+ bind_key = getattr(cls, "__bind_key__", None)
+ parent_metadata = getattr(cls, "metadata", None)
+ metadata = cls.__fsa__._make_metadata(bind_key)
+
+ if metadata is not parent_metadata:
+ cls.metadata = metadata
+
+ super().__init__(name, bases, d, **kwargs)
+
+
+class BindMixin:
+ """DeclarativeBase mixin to set a model's ``metadata`` based on ``__bind_key__``.
+
+ If no ``__bind_key__`` is specified, the model will use the default metadata
+ provided by ``DeclarativeBase`` or ``DeclarativeBaseNoMeta``.
+ If the model doesn't set ``metadata`` or ``__table__`` directly
+ and does set ``__bind_key__``, the model will use the metadata
+ for the specified bind key.
+ If the ``metadata`` is the same as the parent model, it will not be set
+ directly on the child model.
+
+ .. versionchanged:: 3.1.0
+ """
+
+ __fsa__: SQLAlchemy
+ metadata: sa.MetaData
+
+ @classmethod
+ def __init_subclass__(cls: t.Type[BindMixin], **kwargs: t.Dict[str, t.Any]) -> None:
+ if not ("metadata" in cls.__dict__ or "__table__" in cls.__dict__) and hasattr(
+ cls, "__bind_key__"
+ ):
+ bind_key = getattr(cls, "__bind_key__", None)
+ parent_metadata = getattr(cls, "metadata", None)
+ metadata = cls.__fsa__._make_metadata(bind_key)
+
+ if metadata is not parent_metadata:
+ cls.metadata = metadata
+
+ super().__init_subclass__(**kwargs)
+
+
+class NameMetaMixin(type):
+ """Metaclass mixin that sets a model's ``__tablename__`` by converting the
+ ``CamelCase`` class name to ``snake_case``. A name is set for non-abstract models
+ that do not otherwise define ``__tablename__``. If a model does not define a primary
+ key, it will not generate a name or ``__table__``, for single-table inheritance.
+ """
+
+ metadata: sa.MetaData
+ __tablename__: str
+ __table__: sa.Table
+
+ def __init__(
+ cls, name: str, bases: tuple[type, ...], d: dict[str, t.Any], **kwargs: t.Any
+ ) -> None:
+ if should_set_tablename(cls):
+ cls.__tablename__ = camel_to_snake_case(cls.__name__)
+
+ super().__init__(name, bases, d, **kwargs)
+
+ # __table_cls__ has run. If no table was created, use the parent table.
+ if (
+ "__tablename__" not in cls.__dict__
+ and "__table__" in cls.__dict__
+ and cls.__dict__["__table__"] is None
+ ):
+ del cls.__table__
+
+ def __table_cls__(cls, *args: t.Any, **kwargs: t.Any) -> sa.Table | None:
+ """This is called by SQLAlchemy during mapper setup. It determines the final
+ table object that the model will use.
+
+ If no primary key is found, that indicates single-table inheritance, so no table
+ will be created and ``__tablename__`` will be unset.
+ """
+ schema = kwargs.get("schema")
+
+ if schema is None:
+ key = args[0]
+ else:
+ key = f"{schema}.{args[0]}"
+
+ # Check if a table with this name already exists. Allows reflected tables to be
+ # applied to models by name.
+ if key in cls.metadata.tables:
+ return sa.Table(*args, **kwargs)
+
+ # If a primary key is found, create a table for joined-table inheritance.
+ for arg in args:
+ if (isinstance(arg, sa.Column) and arg.primary_key) or isinstance(
+ arg, sa.PrimaryKeyConstraint
+ ):
+ return sa.Table(*args, **kwargs)
+
+ # If no base classes define a table, return one that's missing a primary key
+ # so SQLAlchemy shows the correct error.
+ for base in cls.__mro__[1:-1]:
+ if "__table__" in base.__dict__:
+ break
+ else:
+ return sa.Table(*args, **kwargs)
+
+ # Single-table inheritance, use the parent table name. __init__ will unset
+ # __table__ based on this.
+ if "__tablename__" in cls.__dict__:
+ del cls.__tablename__
+
+ return None
+
+
+class NameMixin:
+ """DeclarativeBase mixin that sets a model's ``__tablename__`` by converting the
+ ``CamelCase`` class name to ``snake_case``. A name is set for non-abstract models
+ that do not otherwise define ``__tablename__``. If a model does not define a primary
+ key, it will not generate a name or ``__table__``, for single-table inheritance.
+
+ .. versionchanged:: 3.1.0
+ """
+
+ metadata: sa.MetaData
+ __tablename__: str
+ __table__: sa.Table
+
+ @classmethod
+ def __init_subclass__(cls: t.Type[NameMixin], **kwargs: t.Dict[str, t.Any]) -> None:
+ if should_set_tablename(cls):
+ cls.__tablename__ = camel_to_snake_case(cls.__name__)
+
+ super().__init_subclass__(**kwargs)
+
+ # __table_cls__ has run. If no table was created, use the parent table.
+ if (
+ "__tablename__" not in cls.__dict__
+ and "__table__" in cls.__dict__
+ and cls.__dict__["__table__"] is None
+ ):
+ del cls.__table__
+
+ @classmethod
+ def __table_cls__(cls, *args: t.Any, **kwargs: t.Any) -> sa.Table | None:
+ """This is called by SQLAlchemy during mapper setup. It determines the final
+ table object that the model will use.
+
+ If no primary key is found, that indicates single-table inheritance, so no table
+ will be created and ``__tablename__`` will be unset.
+ """
+ schema = kwargs.get("schema")
+
+ if schema is None:
+ key = args[0]
+ else:
+ key = f"{schema}.{args[0]}"
+
+ # Check if a table with this name already exists. Allows reflected tables to be
+ # applied to models by name.
+ if key in cls.metadata.tables:
+ return sa.Table(*args, **kwargs)
+
+ # If a primary key is found, create a table for joined-table inheritance.
+ for arg in args:
+ if (isinstance(arg, sa.Column) and arg.primary_key) or isinstance(
+ arg, sa.PrimaryKeyConstraint
+ ):
+ return sa.Table(*args, **kwargs)
+
+ # If no base classes define a table, return one that's missing a primary key
+ # so SQLAlchemy shows the correct error.
+ for base in cls.__mro__[1:-1]:
+ if "__table__" in base.__dict__:
+ break
+ else:
+ return sa.Table(*args, **kwargs)
+
+ # Single-table inheritance, use the parent table name. __init__ will unset
+ # __table__ based on this.
+ if "__tablename__" in cls.__dict__:
+ del cls.__tablename__
+
+ return None
+
+
+def should_set_tablename(cls: type) -> bool:
+ """Determine whether ``__tablename__`` should be generated for a model.
+
+ - If no class in the MRO sets a name, one should be generated.
+ - If a declared attr is found, it should be used instead.
+ - If a name is found, it should be used if the class is a mixin, otherwise one
+ should be generated.
+ - Abstract models should not have one generated.
+
+ Later, ``__table_cls__`` will determine if the model looks like single or
+ joined-table inheritance. If no primary key is found, the name will be unset.
+ """
+ if (
+ cls.__dict__.get("__abstract__", False)
+ or (
+ not issubclass(cls, (sa_orm.DeclarativeBase, sa_orm.DeclarativeBaseNoMeta))
+ and not any(isinstance(b, sa_orm.DeclarativeMeta) for b in cls.__mro__[1:])
+ )
+ or any(
+ (b is sa_orm.DeclarativeBase or b is sa_orm.DeclarativeBaseNoMeta)
+ for b in cls.__bases__
+ )
+ ):
+ return False
+
+ for base in cls.__mro__:
+ if "__tablename__" not in base.__dict__:
+ continue
+
+ if isinstance(base.__dict__["__tablename__"], sa_orm.declared_attr):
+ return False
+
+ return not (
+ base is cls
+ or base.__dict__.get("__abstract__", False)
+ or not (
+ # SQLAlchemy 1.x
+ isinstance(base, sa_orm.DeclarativeMeta)
+ # 2.x: DeclarativeBas uses this as metaclass
+ or isinstance(base, sa_orm.decl_api.DeclarativeAttributeIntercept)
+ # 2.x: DeclarativeBaseNoMeta doesn't use a metaclass
+ or issubclass(base, sa_orm.DeclarativeBaseNoMeta)
+ )
+ )
+
+ return True
+
+
+def camel_to_snake_case(name: str) -> str:
+ """Convert a ``CamelCase`` name to ``snake_case``."""
+ name = re.sub(r"((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))", r"_\1", name)
+ return name.lower().lstrip("_")
+
+
+class DefaultMeta(BindMetaMixin, NameMetaMixin, sa_orm.DeclarativeMeta):
+ """SQLAlchemy declarative metaclass that provides ``__bind_key__`` and
+ ``__tablename__`` support.
+ """
+
+
+class DefaultMetaNoName(BindMetaMixin, sa_orm.DeclarativeMeta):
+ """SQLAlchemy declarative metaclass that provides ``__bind_key__`` and
+ ``__tablename__`` support.
+ """
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/pagination.py b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/pagination.py
new file mode 100644
index 0000000..3d49d6e
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/pagination.py
@@ -0,0 +1,364 @@
+from __future__ import annotations
+
+import typing as t
+from math import ceil
+
+import sqlalchemy as sa
+import sqlalchemy.orm as sa_orm
+from flask import abort
+from flask import request
+
+
+class Pagination:
+ """Apply an offset and limit to the query based on the current page and number of
+ items per page.
+
+ Don't create pagination objects manually. They are created by
+ :meth:`.SQLAlchemy.paginate` and :meth:`.Query.paginate`.
+
+ This is a base class, a subclass must implement :meth:`_query_items` and
+ :meth:`_query_count`. Those methods will use arguments passed as ``kwargs`` to
+ perform the queries.
+
+ :param page: The current page, used to calculate the offset. Defaults to the
+ ``page`` query arg during a request, or 1 otherwise.
+ :param per_page: The maximum number of items on a page, used to calculate the
+ offset and limit. Defaults to the ``per_page`` query arg during a request,
+ or 20 otherwise.
+ :param max_per_page: The maximum allowed value for ``per_page``, to limit a
+ user-provided value. Use ``None`` for no limit. Defaults to 100.
+ :param error_out: Abort with a ``404 Not Found`` error if no items are returned
+ and ``page`` is not 1, or if ``page`` or ``per_page`` is less than 1, or if
+ either are not ints.
+ :param count: Calculate the total number of values by issuing an extra count
+ query. For very complex queries this may be inaccurate or slow, so it can be
+ disabled and set manually if necessary.
+ :param kwargs: Information about the query to paginate. Different subclasses will
+ require different arguments.
+
+ .. versionchanged:: 3.0
+ Iterating over a pagination object iterates over its items.
+
+ .. versionchanged:: 3.0
+ Creating instances manually is not a public API.
+ """
+
+ def __init__(
+ self,
+ page: int | None = None,
+ per_page: int | None = None,
+ max_per_page: int | None = 100,
+ error_out: bool = True,
+ count: bool = True,
+ **kwargs: t.Any,
+ ) -> None:
+ self._query_args = kwargs
+ page, per_page = self._prepare_page_args(
+ page=page,
+ per_page=per_page,
+ max_per_page=max_per_page,
+ error_out=error_out,
+ )
+
+ self.page: int = page
+ """The current page."""
+
+ self.per_page: int = per_page
+ """The maximum number of items on a page."""
+
+ self.max_per_page: int | None = max_per_page
+ """The maximum allowed value for ``per_page``."""
+
+ items = self._query_items()
+
+ if not items and page != 1 and error_out:
+ abort(404)
+
+ self.items: list[t.Any] = items
+ """The items on the current page. Iterating over the pagination object is
+ equivalent to iterating over the items.
+ """
+
+ if count:
+ total = self._query_count()
+ else:
+ total = None
+
+ self.total: int | None = total
+ """The total number of items across all pages."""
+
+ @staticmethod
+ def _prepare_page_args(
+ *,
+ page: int | None = None,
+ per_page: int | None = None,
+ max_per_page: int | None = None,
+ error_out: bool = True,
+ ) -> tuple[int, int]:
+ if request:
+ if page is None:
+ try:
+ page = int(request.args.get("page", 1))
+ except (TypeError, ValueError):
+ if error_out:
+ abort(404)
+
+ page = 1
+
+ if per_page is None:
+ try:
+ per_page = int(request.args.get("per_page", 20))
+ except (TypeError, ValueError):
+ if error_out:
+ abort(404)
+
+ per_page = 20
+ else:
+ if page is None:
+ page = 1
+
+ if per_page is None:
+ per_page = 20
+
+ if max_per_page is not None:
+ per_page = min(per_page, max_per_page)
+
+ if page < 1:
+ if error_out:
+ abort(404)
+ else:
+ page = 1
+
+ if per_page < 1:
+ if error_out:
+ abort(404)
+ else:
+ per_page = 20
+
+ return page, per_page
+
+ @property
+ def _query_offset(self) -> int:
+ """The index of the first item to query, passed to ``offset()``.
+
+ :meta private:
+
+ .. versionadded:: 3.0
+ """
+ return (self.page - 1) * self.per_page
+
+ def _query_items(self) -> list[t.Any]:
+ """Execute the query to get the items on the current page.
+
+ Uses init arguments stored in :attr:`_query_args`.
+
+ :meta private:
+
+ .. versionadded:: 3.0
+ """
+ raise NotImplementedError
+
+ def _query_count(self) -> int:
+ """Execute the query to get the total number of items.
+
+ Uses init arguments stored in :attr:`_query_args`.
+
+ :meta private:
+
+ .. versionadded:: 3.0
+ """
+ raise NotImplementedError
+
+ @property
+ def first(self) -> int:
+ """The number of the first item on the page, starting from 1, or 0 if there are
+ no items.
+
+ .. versionadded:: 3.0
+ """
+ if len(self.items) == 0:
+ return 0
+
+ return (self.page - 1) * self.per_page + 1
+
+ @property
+ def last(self) -> int:
+ """The number of the last item on the page, starting from 1, inclusive, or 0 if
+ there are no items.
+
+ .. versionadded:: 3.0
+ """
+ first = self.first
+ return max(first, first + len(self.items) - 1)
+
+ @property
+ def pages(self) -> int:
+ """The total number of pages."""
+ if self.total == 0 or self.total is None:
+ return 0
+
+ return ceil(self.total / self.per_page)
+
+ @property
+ def has_prev(self) -> bool:
+ """``True`` if this is not the first page."""
+ return self.page > 1
+
+ @property
+ def prev_num(self) -> int | None:
+ """The previous page number, or ``None`` if this is the first page."""
+ if not self.has_prev:
+ return None
+
+ return self.page - 1
+
+ def prev(self, *, error_out: bool = False) -> Pagination:
+ """Query the :class:`Pagination` object for the previous page.
+
+ :param error_out: Abort with a ``404 Not Found`` error if no items are returned
+ and ``page`` is not 1, or if ``page`` or ``per_page`` is less than 1, or if
+ either are not ints.
+ """
+ p = type(self)(
+ page=self.page - 1,
+ per_page=self.per_page,
+ error_out=error_out,
+ count=False,
+ **self._query_args,
+ )
+ p.total = self.total
+ return p
+
+ @property
+ def has_next(self) -> bool:
+ """``True`` if this is not the last page."""
+ return self.page < self.pages
+
+ @property
+ def next_num(self) -> int | None:
+ """The next page number, or ``None`` if this is the last page."""
+ if not self.has_next:
+ return None
+
+ return self.page + 1
+
+ def next(self, *, error_out: bool = False) -> Pagination:
+ """Query the :class:`Pagination` object for the next page.
+
+ :param error_out: Abort with a ``404 Not Found`` error if no items are returned
+ and ``page`` is not 1, or if ``page`` or ``per_page`` is less than 1, or if
+ either are not ints.
+ """
+ p = type(self)(
+ page=self.page + 1,
+ per_page=self.per_page,
+ max_per_page=self.max_per_page,
+ error_out=error_out,
+ count=False,
+ **self._query_args,
+ )
+ p.total = self.total
+ return p
+
+ def iter_pages(
+ self,
+ *,
+ left_edge: int = 2,
+ left_current: int = 2,
+ right_current: int = 4,
+ right_edge: int = 2,
+ ) -> t.Iterator[int | None]:
+ """Yield page numbers for a pagination widget. Skipped pages between the edges
+ and middle are represented by a ``None``.
+
+ For example, if there are 20 pages and the current page is 7, the following
+ values are yielded.
+
+ .. code-block:: python
+
+ 1, 2, None, 5, 6, 7, 8, 9, 10, 11, None, 19, 20
+
+ :param left_edge: How many pages to show from the first page.
+ :param left_current: How many pages to show left of the current page.
+ :param right_current: How many pages to show right of the current page.
+ :param right_edge: How many pages to show from the last page.
+
+ .. versionchanged:: 3.0
+ Improved efficiency of calculating what to yield.
+
+ .. versionchanged:: 3.0
+ ``right_current`` boundary is inclusive.
+
+ .. versionchanged:: 3.0
+ All parameters are keyword-only.
+ """
+ pages_end = self.pages + 1
+
+ if pages_end == 1:
+ return
+
+ left_end = min(1 + left_edge, pages_end)
+ yield from range(1, left_end)
+
+ if left_end == pages_end:
+ return
+
+ mid_start = max(left_end, self.page - left_current)
+ mid_end = min(self.page + right_current + 1, pages_end)
+
+ if mid_start - left_end > 0:
+ yield None
+
+ yield from range(mid_start, mid_end)
+
+ if mid_end == pages_end:
+ return
+
+ right_start = max(mid_end, pages_end - right_edge)
+
+ if right_start - mid_end > 0:
+ yield None
+
+ yield from range(right_start, pages_end)
+
+ def __iter__(self) -> t.Iterator[t.Any]:
+ yield from self.items
+
+
+class SelectPagination(Pagination):
+ """Returned by :meth:`.SQLAlchemy.paginate`. Takes ``select`` and ``session``
+ arguments in addition to the :class:`Pagination` arguments.
+
+ .. versionadded:: 3.0
+ """
+
+ def _query_items(self) -> list[t.Any]:
+ select = self._query_args["select"]
+ select = select.limit(self.per_page).offset(self._query_offset)
+ session = self._query_args["session"]
+ return list(session.execute(select).unique().scalars())
+
+ def _query_count(self) -> int:
+ select = self._query_args["select"]
+ sub = select.options(sa_orm.lazyload("*")).order_by(None).subquery()
+ session = self._query_args["session"]
+ out = session.execute(sa.select(sa.func.count()).select_from(sub)).scalar()
+ return out # type: ignore[no-any-return]
+
+
+class QueryPagination(Pagination):
+ """Returned by :meth:`.Query.paginate`. Takes a ``query`` argument in addition to
+ the :class:`Pagination` arguments.
+
+ .. versionadded:: 3.0
+ """
+
+ def _query_items(self) -> list[t.Any]:
+ query = self._query_args["query"]
+ out = query.limit(self.per_page).offset(self._query_offset).all()
+ return out # type: ignore[no-any-return]
+
+ def _query_count(self) -> int:
+ # Query.count automatically disables eager loads
+ out = self._query_args["query"].order_by(None).count()
+ return out # type: ignore[no-any-return]
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/py.typed b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/query.py b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/query.py
new file mode 100644
index 0000000..35f927d
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/query.py
@@ -0,0 +1,105 @@
+from __future__ import annotations
+
+import typing as t
+
+import sqlalchemy.exc as sa_exc
+import sqlalchemy.orm as sa_orm
+from flask import abort
+
+from .pagination import Pagination
+from .pagination import QueryPagination
+
+
+class Query(sa_orm.Query): # type: ignore[type-arg]
+ """SQLAlchemy :class:`~sqlalchemy.orm.query.Query` subclass with some extra methods
+ useful for querying in a web application.
+
+ This is the default query class for :attr:`.Model.query`.
+
+ .. versionchanged:: 3.0
+ Renamed to ``Query`` from ``BaseQuery``.
+ """
+
+ def get_or_404(self, ident: t.Any, description: str | None = None) -> t.Any:
+ """Like :meth:`~sqlalchemy.orm.Query.get` but aborts with a ``404 Not Found``
+ error instead of returning ``None``.
+
+ :param ident: The primary key to query.
+ :param description: A custom message to show on the error page.
+ """
+ rv = self.get(ident)
+
+ if rv is None:
+ abort(404, description=description)
+
+ return rv
+
+ def first_or_404(self, description: str | None = None) -> t.Any:
+ """Like :meth:`~sqlalchemy.orm.Query.first` but aborts with a ``404 Not Found``
+ error instead of returning ``None``.
+
+ :param description: A custom message to show on the error page.
+ """
+ rv = self.first()
+
+ if rv is None:
+ abort(404, description=description)
+
+ return rv
+
+ def one_or_404(self, description: str | None = None) -> t.Any:
+ """Like :meth:`~sqlalchemy.orm.Query.one` but aborts with a ``404 Not Found``
+ error instead of raising ``NoResultFound`` or ``MultipleResultsFound``.
+
+ :param description: A custom message to show on the error page.
+
+ .. versionadded:: 3.0
+ """
+ try:
+ return self.one()
+ except (sa_exc.NoResultFound, sa_exc.MultipleResultsFound):
+ abort(404, description=description)
+
+ def paginate(
+ self,
+ *,
+ page: int | None = None,
+ per_page: int | None = None,
+ max_per_page: int | None = None,
+ error_out: bool = True,
+ count: bool = True,
+ ) -> Pagination:
+ """Apply an offset and limit to the query based on the current page and number
+ of items per page, returning a :class:`.Pagination` object.
+
+ :param page: The current page, used to calculate the offset. Defaults to the
+ ``page`` query arg during a request, or 1 otherwise.
+ :param per_page: The maximum number of items on a page, used to calculate the
+ offset and limit. Defaults to the ``per_page`` query arg during a request,
+ or 20 otherwise.
+ :param max_per_page: The maximum allowed value for ``per_page``, to limit a
+ user-provided value. Use ``None`` for no limit. Defaults to 100.
+ :param error_out: Abort with a ``404 Not Found`` error if no items are returned
+ and ``page`` is not 1, or if ``page`` or ``per_page`` is less than 1, or if
+ either are not ints.
+ :param count: Calculate the total number of values by issuing an extra count
+ query. For very complex queries this may be inaccurate or slow, so it can be
+ disabled and set manually if necessary.
+
+ .. versionchanged:: 3.0
+ All parameters are keyword-only.
+
+ .. versionchanged:: 3.0
+ The ``count`` query is more efficient.
+
+ .. versionchanged:: 3.0
+ ``max_per_page`` defaults to 100.
+ """
+ return QueryPagination(
+ query=self,
+ page=page,
+ per_page=per_page,
+ max_per_page=max_per_page,
+ error_out=error_out,
+ count=count,
+ )
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/record_queries.py b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/record_queries.py
new file mode 100644
index 0000000..e8273be
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/record_queries.py
@@ -0,0 +1,117 @@
+from __future__ import annotations
+
+import dataclasses
+import inspect
+import typing as t
+from time import perf_counter
+
+import sqlalchemy as sa
+import sqlalchemy.event as sa_event
+from flask import current_app
+from flask import g
+from flask import has_app_context
+
+
+def get_recorded_queries() -> list[_QueryInfo]:
+ """Get the list of recorded query information for the current session. Queries are
+ recorded if the config :data:`.SQLALCHEMY_RECORD_QUERIES` is enabled.
+
+ Each query info object has the following attributes:
+
+ ``statement``
+ The string of SQL generated by SQLAlchemy with parameter placeholders.
+ ``parameters``
+ The parameters sent with the SQL statement.
+ ``start_time`` / ``end_time``
+ Timing info about when the query started execution and when the results where
+ returned. Accuracy and value depends on the operating system.
+ ``duration``
+ The time the query took in seconds.
+ ``location``
+ A string description of where in your application code the query was executed.
+ This may not be possible to calculate, and the format is not stable.
+
+ .. versionchanged:: 3.0
+ Renamed from ``get_debug_queries``.
+
+ .. versionchanged:: 3.0
+ The info object is a dataclass instead of a tuple.
+
+ .. versionchanged:: 3.0
+ The info object attribute ``context`` is renamed to ``location``.
+
+ .. versionchanged:: 3.0
+ Not enabled automatically in debug or testing mode.
+ """
+ return g.get("_sqlalchemy_queries", []) # type: ignore[no-any-return]
+
+
+@dataclasses.dataclass
+class _QueryInfo:
+ """Information about an executed query. Returned by :func:`get_recorded_queries`.
+
+ .. versionchanged:: 3.0
+ Renamed from ``_DebugQueryTuple``.
+
+ .. versionchanged:: 3.0
+ Changed to a dataclass instead of a tuple.
+
+ .. versionchanged:: 3.0
+ ``context`` is renamed to ``location``.
+ """
+
+ statement: str | None
+ parameters: t.Any
+ start_time: float
+ end_time: float
+ location: str
+
+ @property
+ def duration(self) -> float:
+ return self.end_time - self.start_time
+
+
+def _listen(engine: sa.engine.Engine) -> None:
+ sa_event.listen(engine, "before_cursor_execute", _record_start, named=True)
+ sa_event.listen(engine, "after_cursor_execute", _record_end, named=True)
+
+
+def _record_start(context: sa.engine.ExecutionContext, **kwargs: t.Any) -> None:
+ if not has_app_context():
+ return
+
+ context._fsa_start_time = perf_counter() # type: ignore[attr-defined]
+
+
+def _record_end(context: sa.engine.ExecutionContext, **kwargs: t.Any) -> None:
+ if not has_app_context():
+ return
+
+ if "_sqlalchemy_queries" not in g:
+ g._sqlalchemy_queries = []
+
+ import_top = current_app.import_name.partition(".")[0]
+ import_dot = f"{import_top}."
+ frame = inspect.currentframe()
+
+ while frame:
+ name = frame.f_globals.get("__name__")
+
+ if name and (name == import_top or name.startswith(import_dot)):
+ code = frame.f_code
+ location = f"{code.co_filename}:{frame.f_lineno} ({code.co_name})"
+ break
+
+ frame = frame.f_back
+ else:
+ location = ""
+
+ g._sqlalchemy_queries.append(
+ _QueryInfo(
+ statement=context.statement,
+ parameters=context.parameters,
+ start_time=context._fsa_start_time, # type: ignore[attr-defined]
+ end_time=perf_counter(),
+ location=location,
+ )
+ )
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/session.py b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/session.py
new file mode 100644
index 0000000..631fffa
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/session.py
@@ -0,0 +1,111 @@
+from __future__ import annotations
+
+import typing as t
+
+import sqlalchemy as sa
+import sqlalchemy.exc as sa_exc
+import sqlalchemy.orm as sa_orm
+from flask.globals import app_ctx
+
+if t.TYPE_CHECKING:
+ from .extension import SQLAlchemy
+
+
+class Session(sa_orm.Session):
+ """A SQLAlchemy :class:`~sqlalchemy.orm.Session` class that chooses what engine to
+ use based on the bind key associated with the metadata associated with the thing
+ being queried.
+
+ To customize ``db.session``, subclass this and pass it as the ``class_`` key in the
+ ``session_options`` to :class:`.SQLAlchemy`.
+
+ .. versionchanged:: 3.0
+ Renamed from ``SignallingSession``.
+ """
+
+ def __init__(self, db: SQLAlchemy, **kwargs: t.Any) -> None:
+ super().__init__(**kwargs)
+ self._db = db
+ self._model_changes: dict[object, tuple[t.Any, str]] = {}
+
+ def get_bind(
+ self,
+ mapper: t.Any | None = None,
+ clause: t.Any | None = None,
+ bind: sa.engine.Engine | sa.engine.Connection | None = None,
+ **kwargs: t.Any,
+ ) -> sa.engine.Engine | sa.engine.Connection:
+ """Select an engine based on the ``bind_key`` of the metadata associated with
+ the model or table being queried. If no bind key is set, uses the default bind.
+
+ .. versionchanged:: 3.0.3
+ Fix finding the bind for a joined inheritance model.
+
+ .. versionchanged:: 3.0
+ The implementation more closely matches the base SQLAlchemy implementation.
+
+ .. versionchanged:: 2.1
+ Support joining an external transaction.
+ """
+ if bind is not None:
+ return bind
+
+ engines = self._db.engines
+
+ if mapper is not None:
+ try:
+ mapper = sa.inspect(mapper)
+ except sa_exc.NoInspectionAvailable as e:
+ if isinstance(mapper, type):
+ raise sa_orm.exc.UnmappedClassError(mapper) from e
+
+ raise
+
+ engine = _clause_to_engine(mapper.local_table, engines)
+
+ if engine is not None:
+ return engine
+
+ if clause is not None:
+ engine = _clause_to_engine(clause, engines)
+
+ if engine is not None:
+ return engine
+
+ if None in engines:
+ return engines[None]
+
+ return super().get_bind(mapper=mapper, clause=clause, bind=bind, **kwargs)
+
+
+def _clause_to_engine(
+ clause: sa.ClauseElement | None,
+ engines: t.Mapping[str | None, sa.engine.Engine],
+) -> sa.engine.Engine | None:
+ """If the clause is a table, return the engine associated with the table's
+ metadata's bind key.
+ """
+ table = None
+
+ if clause is not None:
+ if isinstance(clause, sa.Table):
+ table = clause
+ elif isinstance(clause, sa.UpdateBase) and isinstance(clause.table, sa.Table):
+ table = clause.table
+
+ if table is not None and "bind_key" in table.metadata.info:
+ key = table.metadata.info["bind_key"]
+
+ if key not in engines:
+ raise sa_exc.UnboundExecutionError(
+ f"Bind key '{key}' is not in 'SQLALCHEMY_BINDS' config."
+ )
+
+ return engines[key]
+
+ return None
+
+
+def _app_ctx_id() -> int:
+ """Get the id of the current Flask application context for the session scope."""
+ return id(app_ctx._get_current_object()) # type: ignore[attr-defined]
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/table.py b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/table.py
new file mode 100644
index 0000000..ab08a69
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/table.py
@@ -0,0 +1,39 @@
+from __future__ import annotations
+
+import typing as t
+
+import sqlalchemy as sa
+import sqlalchemy.sql.schema as sa_sql_schema
+
+
+class _Table(sa.Table):
+ @t.overload
+ def __init__(
+ self,
+ name: str,
+ *args: sa_sql_schema.SchemaItem,
+ bind_key: str | None = None,
+ **kwargs: t.Any,
+ ) -> None:
+ ...
+
+ @t.overload
+ def __init__(
+ self,
+ name: str,
+ metadata: sa.MetaData,
+ *args: sa_sql_schema.SchemaItem,
+ **kwargs: t.Any,
+ ) -> None:
+ ...
+
+ @t.overload
+ def __init__(
+ self, name: str, *args: sa_sql_schema.SchemaItem, **kwargs: t.Any
+ ) -> None:
+ ...
+
+ def __init__(
+ self, name: str, *args: sa_sql_schema.SchemaItem, **kwargs: t.Any
+ ) -> None:
+ super().__init__(name, *args, **kwargs) # type: ignore[arg-type]
diff --git a/blog-app/venv/Lib/site-packages/flask_sqlalchemy/track_modifications.py b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/track_modifications.py
new file mode 100644
index 0000000..7028b65
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/flask_sqlalchemy/track_modifications.py
@@ -0,0 +1,88 @@
+from __future__ import annotations
+
+import typing as t
+
+import sqlalchemy as sa
+import sqlalchemy.event as sa_event
+import sqlalchemy.orm as sa_orm
+from flask import current_app
+from flask import has_app_context
+from flask.signals import Namespace # type: ignore[attr-defined]
+
+if t.TYPE_CHECKING:
+ from .session import Session
+
+_signals = Namespace()
+
+models_committed = _signals.signal("models-committed")
+"""This Blinker signal is sent after the session is committed if there were changed
+models in the session.
+
+The sender is the application that emitted the changes. The receiver is passed the
+``changes`` argument with a list of tuples in the form ``(instance, operation)``.
+The operations are ``"insert"``, ``"update"``, and ``"delete"``.
+"""
+
+before_models_committed = _signals.signal("before-models-committed")
+"""This signal works exactly like :data:`models_committed` but is emitted before the
+commit takes place.
+"""
+
+
+def _listen(session: sa_orm.scoped_session[Session]) -> None:
+ sa_event.listen(session, "before_flush", _record_ops, named=True)
+ sa_event.listen(session, "before_commit", _record_ops, named=True)
+ sa_event.listen(session, "before_commit", _before_commit)
+ sa_event.listen(session, "after_commit", _after_commit)
+ sa_event.listen(session, "after_rollback", _after_rollback)
+
+
+def _record_ops(session: Session, **kwargs: t.Any) -> None:
+ if not has_app_context():
+ return
+
+ if not current_app.config["SQLALCHEMY_TRACK_MODIFICATIONS"]:
+ return
+
+ for targets, operation in (
+ (session.new, "insert"),
+ (session.dirty, "update"),
+ (session.deleted, "delete"),
+ ):
+ for target in targets:
+ state = sa.inspect(target)
+ key = state.identity_key if state.has_identity else id(target)
+ session._model_changes[key] = (target, operation)
+
+
+def _before_commit(session: Session) -> None:
+ if not has_app_context():
+ return
+
+ app = current_app._get_current_object() # type: ignore[attr-defined]
+
+ if not app.config["SQLALCHEMY_TRACK_MODIFICATIONS"]:
+ return
+
+ if session._model_changes:
+ changes = list(session._model_changes.values())
+ before_models_committed.send(app, changes=changes)
+
+
+def _after_commit(session: Session) -> None:
+ if not has_app_context():
+ return
+
+ app = current_app._get_current_object() # type: ignore[attr-defined]
+
+ if not app.config["SQLALCHEMY_TRACK_MODIFICATIONS"]:
+ return
+
+ if session._model_changes:
+ changes = list(session._model_changes.values())
+ models_committed.send(app, changes=changes)
+ session._model_changes.clear()
+
+
+def _after_rollback(session: Session) -> None:
+ session._model_changes.clear()
diff --git a/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/INSTALLER b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/METADATA b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/METADATA
new file mode 100644
index 0000000..6190cbd
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/METADATA
@@ -0,0 +1,116 @@
+Metadata-Version: 2.4
+Name: greenlet
+Version: 3.2.2
+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 AND Python-2.0
+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/
+Project-URL: Changes, https://greenlet.readthedocs.io/en/latest/changes.html
+Keywords: greenlet coroutine concurrency threads cooperative
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+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.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Operating System :: OS Independent
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=3.9
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+License-File: LICENSE.PSF
+Provides-Extra: docs
+Requires-Dist: Sphinx; extra == "docs"
+Requires-Dist: furo; extra == "docs"
+Provides-Extra: test
+Requires-Dist: objgraph; extra == "test"
+Requires-Dist: psutil; extra == "test"
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: description-content-type
+Dynamic: home-page
+Dynamic: keywords
+Dynamic: license
+Dynamic: license-file
+Dynamic: maintainer
+Dynamic: maintainer-email
+Dynamic: platform
+Dynamic: project-url
+Dynamic: provides-extra
+Dynamic: requires-python
+Dynamic: summary
+
+.. 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/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/RECORD b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/RECORD
new file mode 100644
index 0000000..c376441
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/RECORD
@@ -0,0 +1,120 @@
+../../include/site/python3.10/greenlet/greenlet.h,sha256=sz5pYRSQqedgOt2AMgxLZdTjO-qcr_JMvgiEJR9IAJ8,4755
+greenlet-3.2.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+greenlet-3.2.2.dist-info/METADATA,sha256=X57v7sBEYSNABzNQXpCK_CGTvT2GasTRzF6Qjy9IUys,4193
+greenlet-3.2.2.dist-info/RECORD,,
+greenlet-3.2.2.dist-info/WHEEL,sha256=cApr8aT7ro1gNNNzftTDVqaaXPK4_jWf4Qi4QFW6aVk,101
+greenlet-3.2.2.dist-info/licenses/LICENSE,sha256=dpgx1uXfrywggC-sz_H6-0wgJd2PYlPfpH_K1Z1NCXk,1434
+greenlet-3.2.2.dist-info/licenses/LICENSE.PSF,sha256=5f88I8EQ5JTNfXNsEP2W1GJFe6_soxCEDbZScpjH1Gs,2424
+greenlet-3.2.2.dist-info/top_level.txt,sha256=YSnRsCRoO61JGlP57o8iKL6rdLWDWuiyKD8ekpWUsDc,9
+greenlet/CObjects.cpp,sha256=OPej1bWBgc4sRrTRQ2aFFML9pzDYKlKhlJSjsI0X_eU,3508
+greenlet/PyGreenlet.cpp,sha256=ogWsQ5VhSdItWRLLpWOgSuqYuM3QwQ4cVCxOQIgHx6E,23441
+greenlet/PyGreenlet.hpp,sha256=2ZQlOxYNoy7QwD7mppFoOXe_At56NIsJ0eNsE_hoSsw,1463
+greenlet/PyGreenletUnswitchable.cpp,sha256=PQE0fSZa_IOyUM44IESHkJoD2KtGW3dkhkmZSYY3WHs,4375
+greenlet/PyModule.cpp,sha256=J2TH06dGcNEarioS6NbWXkdME8hJY05XVbdqLrfO5w4,8587
+greenlet/TBrokenGreenlet.cpp,sha256=smN26uC7ahAbNYiS10rtWPjCeTG4jevM8siA2sjJiXg,1021
+greenlet/TExceptionState.cpp,sha256=U7Ctw9fBdNraS0d174MoQW7bN-ae209Ta0JuiKpcpVI,1359
+greenlet/TGreenlet.cpp,sha256=HGYGKpmKYqQ842tASW-QaaV8wua4a5XV_quYKPDsV_Y,25731
+greenlet/TGreenlet.hpp,sha256=7ti9va3tzIdFkQ-FEEEkG5p7vQ3PTdYlDFMHLcnIHw4,28043
+greenlet/TGreenletGlobals.cpp,sha256=YyEmDjKf1g32bsL-unIUScFLnnA1fzLWf2gOMd-D0Zw,3264
+greenlet/TMainGreenlet.cpp,sha256=fvgb8HHB-FVTPEKjR1s_ifCZSpp5D5YQByik0CnIABg,3276
+greenlet/TPythonState.cpp,sha256=vBMJT9qScTSIqhnOTVJqsGug3WbKv9dDt0cOqyhUk8w,15779
+greenlet/TStackState.cpp,sha256=V444I8Jj9DhQz-9leVW_9dtiSRjaE1NMlgDG02Xxq-Y,7381
+greenlet/TThreadState.hpp,sha256=2Jgg7DtGggMYR_x3CLAvAFf1mIdIDtQvSSItcdmX4ZQ,19131
+greenlet/TThreadStateCreator.hpp,sha256=uYTexDWooXSSgUc5uh-Mhm5BQi3-kR6CqpizvNynBFQ,2610
+greenlet/TThreadStateDestroy.cpp,sha256=36yBCAMq3beXTZd-XnFA7DwaHVSOx2vc28-nf0spysU,8169
+greenlet/TUserGreenlet.cpp,sha256=uemg0lwKXtYB0yzmvyYdIIAsKnNkifXM1OJ2OlrFP1A,23553
+greenlet/__init__.py,sha256=NNXC07srcIhPc_tI8r0YWIxSi9S3V0MNFxTFAnY4mFE,1723
+greenlet/__pycache__/__init__.cpython-310.pyc,,
+greenlet/_greenlet.cp310-win_amd64.pyd,sha256=uO9XL6OgIO8vpQPslJVjYp_XH9-ZOdNEpLTkAPw67q4,217088
+greenlet/greenlet.cpp,sha256=WdItb1yWL9WNsTqJNf0Iw8ZwDHD49pkDP0rIRGBg2pw,10996
+greenlet/greenlet.h,sha256=sz5pYRSQqedgOt2AMgxLZdTjO-qcr_JMvgiEJR9IAJ8,4755
+greenlet/greenlet_allocator.hpp,sha256=kxyWW4Qdwlrc7ufgdb5vd6Y7jhauQ699Kod0mqiO1iM,1582
+greenlet/greenlet_compiler_compat.hpp,sha256=nRxpLN9iNbnLVyFDeVmOwyeeNm6scQrOed1l7JQYMCM,4346
+greenlet/greenlet_cpython_compat.hpp,sha256=XrsoFv8nKavrdxly5_-q9lqGeE8d3wKt1YtldsAHAT8,4068
+greenlet/greenlet_exceptions.hpp,sha256=06Bx81DtVaJTa6RtiMcV141b-XHv4ppEgVItkblcLWY,4503
+greenlet/greenlet_internal.hpp,sha256=Ajc-_09W4xWzm9XfyXHAeQAFUgKGKsnJwYsTCoNy3ns,2709
+greenlet/greenlet_refs.hpp,sha256=OnbA91yZf3QHH6-eJccvoNDAaN-pQBMMrclFU1Ot3J4,34436
+greenlet/greenlet_slp_switch.hpp,sha256=kM1QHA2iV-gH4cFyN6lfIagHQxvJZjWOVJdIxRE3TlQ,3198
+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=Z6KN_ud0n6nC3ltJrNz2qtvER6vnRAVRNH9mdIDpMxY,2624
+greenlet/platform/switch_ppc_unix.h,sha256=-ZG7MSSPEA5N4qO9PQChtyEJ-Fm6qInhyZm_ZBHTtMg,2652
+greenlet/platform/switch_riscv_unix.h,sha256=606V6ACDf79Fz_WGItnkgbjIJ0pGg_sHmPyDxQYKK58,949
+greenlet/platform/switch_s390_unix.h,sha256=RRlGu957ybmq95qNNY4Qw1mcaoT3eBnW5KbVwu48KX8,2763
+greenlet/platform/switch_sh_gcc.h,sha256=mcRJBTu-2UBf4kZtX601qofwuDuy-Y-hnxJtrcaB7do,901
+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=J01Fd1y2sFLxkBfsixdpexCVWaAdeprDTEly-ujDQAk,3841
+greenlet/tests/__init__.py,sha256=sqxm7-dZuGBwmNI0n6xrcQJGoHHjoXUGyUTnvHidcYM,9361
+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.cp310-win_amd64.pyd,sha256=adYY1kQrWcD9DtOmN5_n9PQ38YBWsqmHS0DDsZVIAZY,14336
+greenlet/tests/_test_extension_cpp.cp310-win_amd64.pyd,sha256=wrN6WBz3wEA5uLJH8Wx1JoSgVoNcrnMW95bRLvZrkGc,15872
+greenlet/tests/_test_extension_cpp.cpp,sha256=e0kVnaB8CCaEhE9yHtNyfqTjevsPDKKx-zgxk7PPK48,6565
+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=inbfM7_oVzd8jIKGxCgo4JqpFZaDAnWPkSULJ8vIE1s,11964
+greenlet/tests/test_contextvars.py,sha256=xutO-qZgKTwKsA9lAqTjIcTBEiQV4RpNKM-vO2_YCVU,10541
+greenlet/tests/test_cpp.py,sha256=hpxhFAdKJTpAVZP8CBGs1ZcrKdscI9BaDZk4btkI5d4,2736
+greenlet/tests/test_extension_interface.py,sha256=eJ3cwLacdK2WbsrC-4DgeyHdwLRcG4zx7rrkRtqSzC4,3829
+greenlet/tests/test_gc.py,sha256=PCOaRpIyjNnNlDogGL3FZU_lrdXuM-pv1rxeE5TP5mc,2923
+greenlet/tests/test_generator.py,sha256=tONXiTf98VGm347o1b-810daPiwdla5cbpFg6QI1R1g,1240
+greenlet/tests/test_generator_nested.py,sha256=7v4HOYrf1XZP39dk5IUMubdZ8yc3ynwZcqj9GUJyMSA,3718
+greenlet/tests/test_greenlet.py,sha256=rYWDvMx7ZpMlQju9KRxsBR61ela7HSJCg98JtR7RPOQ,46251
+greenlet/tests/test_greenlet_trash.py,sha256=n2dBlQfOoEO1ODatFi8QdhboH3fB86YtqzcYMYOXxbw,7947
+greenlet/tests/test_leaks.py,sha256=Qeso_qH9MCWJOkk2I3VcTh7UhaNvWxrzAmNBta-fUyY,17714
+greenlet/tests/test_stack_saved.py,sha256=eyzqNY2VCGuGlxhT_In6TvZ6Okb0AXFZVyBEnK1jDwA,446
+greenlet/tests/test_throw.py,sha256=u2TQ_WvvCd6N6JdXWIxVEcXkKu5fepDlz9dktYdmtng,3712
+greenlet/tests/test_tracing.py,sha256=VlwzMU0C1noospZhuUMyB7MHw200emIvGCN_6G2p2ZU,8250
+greenlet/tests/test_version.py,sha256=O9DpAITsOFgiRcjd4odQ7ejmwx_N9Q1zQENVcbtFHIc,1339
+greenlet/tests/test_weakref.py,sha256=F8M23btEF87bIbpptLNBORosbQqNZGiYeKMqYjWrsak,883
diff --git a/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/WHEEL b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/WHEEL
new file mode 100644
index 0000000..0460801
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (80.3.1)
+Root-Is-Purelib: false
+Tag: cp310-cp310-win_amd64
+
diff --git a/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/licenses/LICENSE b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/licenses/LICENSE
new file mode 100644
index 0000000..b73a4a1
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/licenses/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/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/licenses/LICENSE.PSF b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/licenses/LICENSE.PSF
new file mode 100644
index 0000000..d3b509a
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/licenses/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/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/top_level.txt b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/top_level.txt
new file mode 100644
index 0000000..46725be
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet-3.2.2.dist-info/top_level.txt
@@ -0,0 +1 @@
+greenlet
diff --git a/blog-app/venv/Lib/site-packages/greenlet/CObjects.cpp b/blog-app/venv/Lib/site-packages/greenlet/CObjects.cpp
new file mode 100644
index 0000000..c135995
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/CObjects.cpp
@@ -0,0 +1,157 @@
+#ifndef COBJECTS_CPP
+#define COBJECTS_CPP
+/*****************************************************************************
+ * C interface
+ *
+ * These are exported using the CObject API
+ */
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+#include "greenlet_exceptions.hpp"
+
+#include "greenlet_internal.hpp"
+#include "greenlet_refs.hpp"
+
+
+#include "TThreadStateDestroy.cpp"
+
+#include "PyGreenlet.hpp"
+
+using greenlet::PyErrOccurred;
+using greenlet::Require;
+
+
+
+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.borrow(), mod_globs->empty_tuple, kwargs.borrow()));
+ }
+ 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 internal_green_throw(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 ****************************************************************/
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+
+#endif
diff --git a/blog-app/venv/Lib/site-packages/greenlet/PyGreenlet.cpp b/blog-app/venv/Lib/site-packages/greenlet/PyGreenlet.cpp
new file mode 100644
index 0000000..29c0bba
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/PyGreenlet.cpp
@@ -0,0 +1,738 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+#ifndef PYGREENLET_CPP
+#define PYGREENLET_CPP
+/*****************
+The Python slot functions for TGreenlet.
+ */
+
+
+#define PY_SSIZE_T_CLEAN
+#include
+#include "structmember.h" // PyMemberDef
+
+#include "greenlet_internal.hpp"
+#include "TThreadStateDestroy.cpp"
+#include "TGreenlet.hpp"
+// #include "TUserGreenlet.cpp"
+// #include "TMainGreenlet.cpp"
+// #include "TBrokenGreenlet.cpp"
+
+
+#include "greenlet_refs.hpp"
+#include "greenlet_slp_switch.hpp"
+
+#include "greenlet_thread_support.hpp"
+#include "TGreenlet.hpp"
+
+#include "TGreenletGlobals.cpp"
+#include "TThreadStateDestroy.cpp"
+#include "PyGreenlet.hpp"
+// #include "TGreenlet.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;
+
+
+
+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;
+}
+
+
+// green_init is used in the tp_init slot. So it's important that
+// it can be called directly from CPython. Thus, we don't use
+// BorrowedGreenlet and BorrowedObject --- although in theory
+// these should be binary layout compatible, that may not be
+// guaranteed to be the case (32-bit linux ppc possibly).
+static int
+green_init(PyGreenlet* self, PyObject* args, PyObject* kwargs)
+{
+ PyArgParseParam run;
+ PyArgParseParam nparent;
+ static const char* 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(PyObject* _self)
+{
+ BorrowedGreenlet self(_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
+internal_green_throw(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 internal_green_throw(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(PyGreenlet* 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(PyGreenlet* self, void* UNUSED(context))
+{
+ try {
+ OwnedObject result(BorrowedGreenlet(self)->run());
+ return result.relinquish_ownership();
+ }
+ catch(const PyErrOccurred&) {
+ return nullptr;
+ }
+}
+
+
+static int
+green_setrun(PyGreenlet* self, PyObject* nrun, void* UNUSED(context))
+{
+ try {
+ BorrowedGreenlet(self)->run(nrun);
+ return 0;
+ }
+ catch(const PyErrOccurred&) {
+ return -1;
+ }
+}
+
+static PyObject*
+green_getparent(PyGreenlet* self, void* UNUSED(context))
+{
+ return BorrowedGreenlet(self)->parent().acquire_or_None();
+}
+
+
+static int
+green_setparent(PyGreenlet* self, PyObject* nparent, void* UNUSED(context))
+{
+ try {
+ BorrowedGreenlet(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(PyGreenlet* self, PyObject* nctx, void* UNUSED(context))
+{
+ try {
+ BorrowedGreenlet(self)->context(nctx);
+ return 0;
+ }
+ catch(const PyErrOccurred&) {
+ return -1;
+ }
+}
+
+
+static PyObject*
+green_getframe(PyGreenlet* self, void* UNUSED(context))
+{
+ const PythonState::OwnedFrame& top_frame = BorrowedGreenlet(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(PyGreenlet* _self)
+{
+ BorrowedGreenlet self(_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;
+}
+
+
+static PyMethodDef green_methods[] = {
+ {
+ .ml_name="switch",
+ .ml_meth=reinterpret_cast(green_switch),
+ .ml_flags=METH_VARARGS | METH_KEYWORDS,
+ .ml_doc=green_switch_doc
+ },
+ {.ml_name="throw", .ml_meth=(PyCFunction)green_throw, .ml_flags=METH_VARARGS, .ml_doc=green_throw_doc},
+ {.ml_name="__getstate__", .ml_meth=(PyCFunction)green_getstate, .ml_flags=METH_NOARGS, .ml_doc=NULL},
+ {.ml_name=NULL, .ml_meth=NULL} /* sentinel */
+};
+
+static PyGetSetDef green_getsets[] = {
+ /* name, getter, setter, doc, context pointer */
+ {.name="__dict__", .get=(getter)green_getdict, .set=(setter)green_setdict},
+ {.name="run", .get=(getter)green_getrun, .set=(setter)green_setrun},
+ {.name="parent", .get=(getter)green_getparent, .set=(setter)green_setparent},
+ {.name="gr_frame", .get=(getter)green_getframe },
+ {
+ .name="gr_context",
+ .get=(getter)green_getcontext,
+ .set=(setter)green_setcontext
+ },
+ {.name="dead", .get=(getter)green_getdead},
+ {.name="_stack_saved", .get=(getter)green_get_stack_saved},
+ {.name=NULL}
+};
+
+static PyMemberDef green_members[] = {
+ {.name=NULL}
+};
+
+static PyNumberMethods green_as_number = {
+ .nb_bool=(inquiry)green_bool,
+};
+
+
+PyTypeObject PyGreenlet_Type = {
+ .ob_base=PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name="greenlet.greenlet", /* tp_name */
+ .tp_basicsize=sizeof(PyGreenlet), /* tp_basicsize */
+ /* methods */
+ .tp_dealloc=(destructor)green_dealloc, /* tp_dealloc */
+ .tp_repr=(reprfunc)green_repr, /* tp_repr */
+ .tp_as_number=&green_as_number, /* tp_as _number*/
+ .tp_flags=G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ .tp_doc="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 */
+ .tp_traverse=(traverseproc)green_traverse, /* tp_traverse */
+ .tp_clear=(inquiry)green_clear, /* tp_clear */
+ .tp_weaklistoffset=offsetof(PyGreenlet, weakreflist), /* tp_weaklistoffset */
+
+ .tp_methods=green_methods, /* tp_methods */
+ .tp_members=green_members, /* tp_members */
+ .tp_getset=green_getsets, /* tp_getset */
+ .tp_dictoffset=offsetof(PyGreenlet, dict), /* tp_dictoffset */
+ .tp_init=(initproc)green_init, /* tp_init */
+ .tp_alloc=PyType_GenericAlloc, /* tp_alloc */
+ .tp_new=(newfunc)green_new, /* tp_new */
+ .tp_free=PyObject_GC_Del, /* tp_free */
+ .tp_is_gc=(inquiry)green_is_gc, /* tp_is_gc */
+};
+
+#endif
+
+// Local Variables:
+// flycheck-clang-include-path: ("/opt/local/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8")
+// End:
diff --git a/blog-app/venv/Lib/site-packages/greenlet/PyGreenlet.hpp b/blog-app/venv/Lib/site-packages/greenlet/PyGreenlet.hpp
new file mode 100644
index 0000000..df6cd80
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/PyGreenlet.hpp
@@ -0,0 +1,35 @@
+#ifndef PYGREENLET_HPP
+#define PYGREENLET_HPP
+
+
+#include "greenlet.h"
+#include "greenlet_compiler_compat.hpp"
+#include "greenlet_refs.hpp"
+
+
+using greenlet::refs::OwnedGreenlet;
+using greenlet::refs::BorrowedGreenlet;
+using greenlet::refs::BorrowedObject;;
+using greenlet::refs::OwnedObject;
+using greenlet::refs::PyErrPieces;
+
+
+// XXX: These doesn't really belong here, it's not a Python slot.
+static OwnedObject internal_green_throw(BorrowedGreenlet self, PyErrPieces& err_pieces);
+
+static PyGreenlet* green_new(PyTypeObject* type, PyObject* UNUSED(args), PyObject* UNUSED(kwds));
+static int green_clear(PyGreenlet* self);
+static int green_init(PyGreenlet* self, PyObject* args, PyObject* kwargs);
+static int green_setparent(PyGreenlet* self, PyObject* nparent, void* UNUSED(context));
+static int green_setrun(PyGreenlet* self, PyObject* nrun, void* UNUSED(context));
+static int green_traverse(PyGreenlet* self, visitproc visit, void* arg);
+static void green_dealloc(PyGreenlet* self);
+static PyObject* green_getparent(PyGreenlet* self, void* UNUSED(context));
+
+static int green_is_gc(PyObject* self);
+static PyObject* green_getdead(PyGreenlet* self, void* UNUSED(context));
+static PyObject* green_getrun(PyGreenlet* self, void* UNUSED(context));
+static int green_setcontext(PyGreenlet* self, PyObject* nctx, void* UNUSED(context));
+static PyObject* green_getframe(PyGreenlet* self, void* UNUSED(context));
+static PyObject* green_repr(PyGreenlet* self);
+#endif
diff --git a/blog-app/venv/Lib/site-packages/greenlet/PyGreenletUnswitchable.cpp b/blog-app/venv/Lib/site-packages/greenlet/PyGreenletUnswitchable.cpp
new file mode 100644
index 0000000..1b768ee
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/PyGreenletUnswitchable.cpp
@@ -0,0 +1,147 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
+/**
+ Implementation of the Python slots for PyGreenletUnswitchable_Type
+*/
+#ifndef PY_GREENLET_UNSWITCHABLE_CPP
+#define PY_GREENLET_UNSWITCHABLE_CPP
+
+
+
+#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_support.hpp"
+#include "TGreenlet.hpp"
+
+#include "TGreenlet.cpp"
+#include "TGreenletGlobals.cpp"
+#include "TThreadStateDestroy.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;
+
+
+#include "PyGreenlet.hpp"
+
+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 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, PyObject* 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, PyObject* 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, closure (context pointer) */
+ {
+ .name="force_switch_error",
+ .get=(getter)green_unswitchable_getforce,
+ .set=(setter)green_unswitchable_setforce,
+ .doc=NULL
+ },
+ {
+ .name="force_slp_switch_error",
+ .get=(getter)green_unswitchable_getforceslp,
+ .set=(setter)green_unswitchable_setforceslp,
+ .doc=nullptr
+ },
+ {.name=nullptr}
+};
+
+PyTypeObject PyGreenletUnswitchable_Type = {
+ .ob_base=PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name="greenlet._greenlet.UnswitchableGreenlet",
+ .tp_dealloc= (destructor)green_dealloc, /* tp_dealloc */
+ .tp_flags=G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ .tp_doc="Undocumented internal class", /* tp_doc */
+ .tp_traverse=(traverseproc)green_traverse, /* tp_traverse */
+ .tp_clear=(inquiry)green_clear, /* tp_clear */
+
+ .tp_getset=green_unswitchable_getsets, /* tp_getset */
+ .tp_base=&PyGreenlet_Type, /* tp_base */
+ .tp_init=(initproc)green_init, /* tp_init */
+ .tp_alloc=PyType_GenericAlloc, /* tp_alloc */
+ .tp_new=(newfunc)green_unswitchable_new, /* tp_new */
+ .tp_free=PyObject_GC_Del, /* tp_free */
+ .tp_is_gc=(inquiry)green_is_gc, /* tp_is_gc */
+};
+
+
+#endif
diff --git a/blog-app/venv/Lib/site-packages/greenlet/PyModule.cpp b/blog-app/venv/Lib/site-packages/greenlet/PyModule.cpp
new file mode 100644
index 0000000..6adcb5c
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/PyModule.cpp
@@ -0,0 +1,292 @@
+#ifndef PY_MODULE_CPP
+#define PY_MODULE_CPP
+
+#include "greenlet_internal.hpp"
+
+
+#include "TGreenletGlobals.cpp"
+#include "TMainGreenlet.cpp"
+#include "TThreadStateDestroy.cpp"
+
+using greenlet::LockGuard;
+using greenlet::ThreadState;
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wunused-function"
+# pragma clang diagnostic ignored "-Wunused-variable"
+#endif
+
+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. Debugging 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;
+}
+
+
+
+
+#if !GREENLET_PY313
+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
+}
+#endif
+
+
+
+
+static PyMethodDef GreenMethods[] = {
+ {
+ .ml_name="getcurrent",
+ .ml_meth=(PyCFunction)mod_getcurrent,
+ .ml_flags=METH_NOARGS,
+ .ml_doc=mod_getcurrent_doc
+ },
+ {
+ .ml_name="settrace",
+ .ml_meth=(PyCFunction)mod_settrace,
+ .ml_flags=METH_VARARGS,
+ .ml_doc=mod_settrace_doc
+ },
+ {
+ .ml_name="gettrace",
+ .ml_meth=(PyCFunction)mod_gettrace,
+ .ml_flags=METH_NOARGS,
+ .ml_doc=mod_gettrace_doc
+ },
+ {
+ .ml_name="set_thread_local",
+ .ml_meth=(PyCFunction)mod_set_thread_local,
+ .ml_flags=METH_VARARGS,
+ .ml_doc=mod_set_thread_local_doc
+ },
+ {
+ .ml_name="get_pending_cleanup_count",
+ .ml_meth=(PyCFunction)mod_get_pending_cleanup_count,
+ .ml_flags=METH_NOARGS,
+ .ml_doc=mod_get_pending_cleanup_count_doc
+ },
+ {
+ .ml_name="get_total_main_greenlets",
+ .ml_meth=(PyCFunction)mod_get_total_main_greenlets,
+ .ml_flags=METH_NOARGS,
+ .ml_doc=mod_get_total_main_greenlets_doc
+ },
+ {
+ .ml_name="get_clocks_used_doing_optional_cleanup",
+ .ml_meth=(PyCFunction)mod_get_clocks_used_doing_optional_cleanup,
+ .ml_flags=METH_NOARGS,
+ .ml_doc=mod_get_clocks_used_doing_optional_cleanup_doc
+ },
+ {
+ .ml_name="enable_optional_cleanup",
+ .ml_meth=(PyCFunction)mod_enable_optional_cleanup,
+ .ml_flags=METH_O,
+ .ml_doc=mod_enable_optional_cleanup_doc
+ },
+#if !GREENLET_PY313
+ {
+ .ml_name="get_tstate_trash_delete_nesting",
+ .ml_meth=(PyCFunction)mod_get_tstate_trash_delete_nesting,
+ .ml_flags=METH_NOARGS,
+ .ml_doc=mod_get_tstate_trash_delete_nesting_doc
+ },
+#endif
+ {.ml_name=NULL, .ml_meth=NULL} /* Sentinel */
+};
+
+static const char* const copy_on_greentype[] = {
+ "getcurrent",
+ "error",
+ "GreenletExit",
+ "settrace",
+ "gettrace",
+ NULL
+};
+
+static struct PyModuleDef greenlet_module_def = {
+ .m_base=PyModuleDef_HEAD_INIT,
+ .m_name="greenlet._greenlet",
+ .m_doc=NULL,
+ .m_size=-1,
+ .m_methods=GreenMethods,
+};
+
+
+#endif
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#elif defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
diff --git a/blog-app/venv/Lib/site-packages/greenlet/TBrokenGreenlet.cpp b/blog-app/venv/Lib/site-packages/greenlet/TBrokenGreenlet.cpp
new file mode 100644
index 0000000..7e9ab5b
--- /dev/null
+++ b/blog-app/venv/Lib/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 "TGreenlet.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/blog-app/venv/Lib/site-packages/greenlet/TExceptionState.cpp b/blog-app/venv/Lib/site-packages/greenlet/TExceptionState.cpp
new file mode 100644
index 0000000..08a94ae
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/TExceptionState.cpp
@@ -0,0 +1,62 @@
+#ifndef GREENLET_EXCEPTION_STATE_CPP
+#define GREENLET_EXCEPTION_STATE_CPP
+
+#include
+#include "TGreenlet.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/blog-app/venv/Lib/site-packages/greenlet/TGreenlet.cpp b/blog-app/venv/Lib/site-packages/greenlet/TGreenlet.cpp
new file mode 100644
index 0000000..4698a17
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/TGreenlet.cpp
@@ -0,0 +1,718 @@
+/* -*- 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"
+*/
+#ifndef TGREENLET_CPP
+#define TGREENLET_CPP
+#include "greenlet_internal.hpp"
+#include "TGreenlet.hpp"
+
+
+#include "TGreenletGlobals.cpp"
+#include "TThreadStateDestroy.cpp"
+
+namespace greenlet {
+
+Greenlet::Greenlet(PyGreenlet* p)
+ : Greenlet(p, StackState())
+{
+}
+
+Greenlet::Greenlet(PyGreenlet* p, const StackState& initial_stack)
+ : _self(p), stack_state(initial_stack)
+{
+ assert(p->pimpl == nullptr);
+ 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();
+ this->_self->pimpl = nullptr;
+}
+
+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;
+ current->expose_frames();
+ }
+ 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\n\tCurrent: %R\n\tExpected: %R",
+ current_main_greenlet, main_greenlet);
+ }
+}
+
+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();
+}
+
+#if GREENLET_PY312
+void GREENLET_NOINLINE(Greenlet::expose_frames)()
+{
+ if (!this->python_state.top_frame()) {
+ return;
+ }
+
+ _PyInterpreterFrame* last_complete_iframe = nullptr;
+ _PyInterpreterFrame* iframe = this->python_state.top_frame()->f_frame;
+ while (iframe) {
+ // We must make a copy before looking at the iframe contents,
+ // since iframe might point to a portion of the greenlet's C stack
+ // that was spilled when switching greenlets.
+ _PyInterpreterFrame iframe_copy;
+ this->stack_state.copy_from_stack(&iframe_copy, iframe, sizeof(*iframe));
+ if (!_PyFrame_IsIncomplete(&iframe_copy)) {
+ // If the iframe were OWNED_BY_CSTACK then it would always be
+ // incomplete. Since it's not incomplete, it's not on the C stack
+ // and we can access it through the original `iframe` pointer
+ // directly. This is important since GetFrameObject might
+ // lazily _create_ the frame object and we don't want the
+ // interpreter to lose track of it.
+ assert(iframe_copy.owner != FRAME_OWNED_BY_CSTACK);
+
+ // We really want to just write:
+ // PyFrameObject* frame = _PyFrame_GetFrameObject(iframe);
+ // but _PyFrame_GetFrameObject calls _PyFrame_MakeAndSetFrameObject
+ // which is not a visible symbol in libpython. The easiest
+ // way to get a public function to call it is using
+ // PyFrame_GetBack, which is defined as follows:
+ // assert(frame != NULL);
+ // assert(!_PyFrame_IsIncomplete(frame->f_frame));
+ // PyFrameObject *back = frame->f_back;
+ // if (back == NULL) {
+ // _PyInterpreterFrame *prev = frame->f_frame->previous;
+ // prev = _PyFrame_GetFirstComplete(prev);
+ // if (prev) {
+ // back = _PyFrame_GetFrameObject(prev);
+ // }
+ // }
+ // return (PyFrameObject*)Py_XNewRef(back);
+ if (!iframe->frame_obj) {
+ PyFrameObject dummy_frame;
+ _PyInterpreterFrame dummy_iframe;
+ dummy_frame.f_back = nullptr;
+ dummy_frame.f_frame = &dummy_iframe;
+ // force the iframe to be considered complete without
+ // needing to check its code object:
+ dummy_iframe.owner = FRAME_OWNED_BY_GENERATOR;
+ dummy_iframe.previous = iframe;
+ assert(!_PyFrame_IsIncomplete(&dummy_iframe));
+ // Drop the returned reference immediately; the iframe
+ // continues to hold a strong reference
+ Py_XDECREF(PyFrame_GetBack(&dummy_frame));
+ assert(iframe->frame_obj);
+ }
+
+ // This is a complete frame, so make the last one of those we saw
+ // point at it, bypassing any incomplete frames (which may have
+ // been on the C stack) in between the two. We're overwriting
+ // last_complete_iframe->previous and need that to be reversible,
+ // so we store the original previous ptr in the frame object
+ // (which we must have created on a previous iteration through
+ // this loop). The frame object has a bunch of storage that is
+ // only used when its iframe is OWNED_BY_FRAME_OBJECT, which only
+ // occurs when the frame object outlives the frame's execution,
+ // which can't have happened yet because the frame is currently
+ // executing as far as the interpreter is concerned. So, we can
+ // reuse it for our own purposes.
+ assert(iframe->owner == FRAME_OWNED_BY_THREAD
+ || iframe->owner == FRAME_OWNED_BY_GENERATOR);
+ if (last_complete_iframe) {
+ assert(last_complete_iframe->frame_obj);
+ memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0],
+ &last_complete_iframe->previous, sizeof(void *));
+ last_complete_iframe->previous = iframe;
+ }
+ last_complete_iframe = iframe;
+ }
+ // Frames that are OWNED_BY_FRAME_OBJECT are linked via the
+ // frame's f_back while all others are linked via the iframe's
+ // previous ptr. Since all the frames we traverse are running
+ // as far as the interpreter is concerned, we don't have to
+ // worry about the OWNED_BY_FRAME_OBJECT case.
+ iframe = iframe_copy.previous;
+ }
+
+ // Give the outermost complete iframe a null previous pointer to
+ // account for any potential incomplete/C-stack iframes between it
+ // and the actual top-of-stack
+ if (last_complete_iframe) {
+ assert(last_complete_iframe->frame_obj);
+ memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0],
+ &last_complete_iframe->previous, sizeof(void *));
+ last_complete_iframe->previous = nullptr;
+ }
+}
+#else
+void Greenlet::expose_frames()
+{
+
+}
+#endif
+
+}; // namespace greenlet
+#endif
diff --git a/blog-app/venv/Lib/site-packages/greenlet/TGreenlet.hpp b/blog-app/venv/Lib/site-packages/greenlet/TGreenlet.hpp
new file mode 100644
index 0000000..f69b881
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/TGreenlet.hpp
@@ -0,0 +1,820 @@
+#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
+# define Py_BUILD_CORE
+# include "internal/pycore_frame.h"
+#endif
+
+#if GREENLET_PY314
+# include "internal/pycore_interpframe_structs.h"
+# include "internal/pycore_interpframe.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_PY314
+ int py_recursion_depth;
+#elif GREENLET_PY312
+ int py_recursion_depth;
+ int c_recursion_depth;
+#else
+ int recursion_depth;
+#endif
+#if GREENLET_PY313
+ PyObject *delete_later;
+#else
+ int trash_delete_nesting;
+#endif
+#if GREENLET_PY311
+ _PyInterpreterFrame* current_frame;
+ _PyStackChunk* datastack_chunk;
+ PyObject** datastack_top;
+ PyObject** datastack_limit;
+#endif
+ // The PyInterpreterFrame list on 3.12+ contains some entries that are
+ // on the C stack, which can't be directly accessed while a greenlet is
+ // suspended. In order to keep greenlet gr_frame introspection working,
+ // we adjust stack switching to rewrite the interpreter frame list
+ // to skip these C-stack frames; we call this "exposing" the greenlet's
+ // frames because it makes them valid to work with in Python. Then when
+ // the greenlet is resumed we need to remember to reverse the operation
+ // we did. The C-stack frames are "entry frames" which are a low-level
+ // interpreter detail; they're not needed for introspection, but do
+ // need to be present for the eval loop to work.
+ void unexpose_frames();
+
+ 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
+
+ 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
+
+ // Fill in [dest, dest + n) with the values that would be at
+ // [src, src + n) while this greenlet is running. This is like memcpy
+ // except that if the greenlet is suspended it accounts for the portion
+ // of the greenlet's stack that was spilled to the heap. `src` may
+ // be on this greenlet's stack, or on the heap, but not on a different
+ // greenlet's stack.
+ void copy_from_stack(void* dest, const void* src, size_t n) const;
+ };
+#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);
+ PyGreenlet* const _self;
+ 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:
+ // This constructor takes ownership of the PyGreenlet, by
+ // setting ``p->pimpl = this;``.
+ 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);
+
+ // Must be called on 3.12+ before exposing a suspended greenlet's
+ // frames to user code. This rewrites the linked list of interpreter
+ // frames to skip the ones that are being stored on the C stack (which
+ // can't be safely accessed while the greenlet is suspended because
+ // that stack space might be hosting a different greenlet), and
+ // sets PythonState::frames_were_exposed so we remember to restore
+ // the original list before resuming the greenlet. The C-stack frames
+ // are a low-level interpreter implementation detail; while they're
+ // important to the bytecode eval loop, they're superfluous for
+ // introspection purposes.
+ void expose_frames();
+
+
+ // 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.
+ inline BorrowedGreenlet self() const noexcept
+ {
+ return BorrowedGreenlet(this->_self);
+ }
+
+ // 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;
+ 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 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 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/blog-app/venv/Lib/site-packages/greenlet/TGreenletGlobals.cpp b/blog-app/venv/Lib/site-packages/greenlet/TGreenletGlobals.cpp
new file mode 100644
index 0000000..0087d2f
--- /dev/null
+++ b/blog-app/venv/Lib/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_internal.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/blog-app/venv/Lib/site-packages/greenlet/TMainGreenlet.cpp b/blog-app/venv/Lib/site-packages/greenlet/TMainGreenlet.cpp
new file mode 100644
index 0000000..a2a9cfe
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/TMainGreenlet.cpp
@@ -0,0 +1,153 @@
+/* -*- 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"
+*/
+#ifndef T_MAIN_GREENLET_CPP
+#define T_MAIN_GREENLET_CPP
+
+#include "TGreenlet.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;
+}
+
+
+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
+
+#endif
diff --git a/blog-app/venv/Lib/site-packages/greenlet/TPythonState.cpp b/blog-app/venv/Lib/site-packages/greenlet/TPythonState.cpp
new file mode 100644
index 0000000..a7f743c
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/TPythonState.cpp
@@ -0,0 +1,402 @@
+#ifndef GREENLET_PYTHON_STATE_CPP
+#define GREENLET_PYTHON_STATE_CPP
+
+#include
+#include "TGreenlet.hpp"
+
+namespace greenlet {
+
+PythonState::PythonState()
+ : _top_frame()
+#if GREENLET_USE_CFRAME
+ ,cframe(nullptr)
+ ,use_tracing(0)
+#endif
+#if GREENLET_PY314
+ ,py_recursion_depth(0)
+#elif GREENLET_PY312
+ ,py_recursion_depth(0)
+ ,c_recursion_depth(0)
+#else
+ ,recursion_depth(0)
+#endif
+#if GREENLET_PY313
+ ,delete_later(nullptr)
+#else
+ ,trash_delete_nesting(0)
+#endif
+#if GREENLET_PY311
+ ,current_frame(nullptr)
+ ,datastack_chunk(nullptr)
+ ,datastack_top(nullptr)
+ ,datastack_limit(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_PY314
+ this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
+ #elif GREENLET_PY312
+ this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
+ this->c_recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
+ #else // not 312
+ this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining;
+ #endif // GREENLET_PY312
+ #if GREENLET_PY313
+ this->current_frame = tstate->current_frame;
+ #elif GREENLET_USE_CFRAME
+ this->current_frame = tstate->cframe->current_frame;
+ #endif
+ 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_PY313
+ this->delete_later = Py_XNewRef(tstate->delete_later);
+ #elif 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
+}
+
+#if GREENLET_PY312
+void GREENLET_NOINLINE(PythonState::unexpose_frames)()
+{
+ if (!this->top_frame()) {
+ return;
+ }
+
+ // See GreenletState::expose_frames() and the comment on frames_were_exposed
+ // for more information about this logic.
+ _PyInterpreterFrame *iframe = this->_top_frame->f_frame;
+ while (iframe != nullptr) {
+ _PyInterpreterFrame *prev_exposed = iframe->previous;
+ assert(iframe->frame_obj);
+ memcpy(&iframe->previous, &iframe->frame_obj->_f_frame_data[0],
+ sizeof(void *));
+ iframe = prev_exposed;
+ }
+}
+#else
+void PythonState::unexpose_frames()
+{}
+#endif
+
+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_PY314
+ tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth;
+ this->unexpose_frames();
+ #elif GREENLET_PY312
+ tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth;
+ tstate->c_recursion_remaining = Py_C_RECURSION_LIMIT - this->c_recursion_depth;
+ this->unexpose_frames();
+ #else // \/ 3.11
+ tstate->recursion_remaining = tstate->recursion_limit - this->recursion_depth;
+ #endif // GREENLET_PY312
+ #if GREENLET_PY313
+ tstate->current_frame = this->current_frame;
+ #elif GREENLET_USE_CFRAME
+ tstate->cframe->current_frame = this->current_frame;
+ #endif
+ 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_PY313
+ Py_XDECREF(tstate->delete_later);
+ tstate->delete_later = this->delete_later;
+ Py_CLEAR(this->delete_later);
+ #elif 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_PY314
+ this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
+#elif GREENLET_PY312
+ this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
+ // XXX: TODO: Comment from a reviewer:
+ // Should this be ``Py_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/blog-app/venv/Lib/site-packages/greenlet/TStackState.cpp b/blog-app/venv/Lib/site-packages/greenlet/TStackState.cpp
new file mode 100644
index 0000000..9743ab5
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/TStackState.cpp
@@ -0,0 +1,265 @@
+#ifndef GREENLET_STACK_STATE_CPP
+#define GREENLET_STACK_STATE_CPP
+
+#include "TGreenlet.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();
+ }
+}
+
+void StackState::copy_from_stack(void* vdest, const void* vsrc, size_t n) const
+{
+ char* dest = static_cast(vdest);
+ const char* src = static_cast(vsrc);
+ if (src + n <= this->_stack_start
+ || src >= this->_stack_start + this->_stack_saved
+ || this->_stack_saved == 0) {
+ // Nothing we're copying was spilled from the stack
+ memcpy(dest, src, n);
+ return;
+ }
+
+ if (src < this->_stack_start) {
+ // Copy the part before the saved stack.
+ // We know src + n > _stack_start due to the test above.
+ const size_t nbefore = this->_stack_start - src;
+ memcpy(dest, src, nbefore);
+ dest += nbefore;
+ src += nbefore;
+ n -= nbefore;
+ }
+ // We know src >= _stack_start after the before-copy, and
+ // src < _stack_start + _stack_saved due to the first if condition
+ size_t nspilled = std::min(n, this->_stack_start + this->_stack_saved - src);
+ memcpy(dest, this->stack_copy + (src - this->_stack_start), nspilled);
+ dest += nspilled;
+ src += nspilled;
+ n -= nspilled;
+ if (n > 0) {
+ // Copy the part after the saved stack
+ memcpy(dest, src, n);
+ }
+}
+
+}; // namespace greenlet
+
+#endif // GREENLET_STACK_STATE_CPP
diff --git a/blog-app/venv/Lib/site-packages/greenlet/TThreadState.hpp b/blog-app/venv/Lib/site-packages/greenlet/TThreadState.hpp
new file mode 100644
index 0000000..e4e6f6c
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/TThreadState.hpp
@@ -0,0 +1,497 @@
+#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);
+
+
+ // Allocates a main greenlet for the thread state. If this fails,
+ // exits the process. Called only during constructing a ThreadState.
+ MainGreenlet* alloc_main()
+ {
+ PyGreenlet* gmain;
+
+ /* create the main greenlet for this thread */
+ gmain = reinterpret_cast(PyType_GenericAlloc(&PyGreenlet_Type, 0));
+ if (gmain == NULL) {
+ throw PyFatalError("alloc_main failed to alloc"); //exits the process
+ }
+
+ MainGreenlet* const main = new MainGreenlet(gmain, this);
+
+ assert(Py_REFCNT(gmain) == 1);
+ assert(gmain->pimpl == main);
+ return main;
+ }
+
+
+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()
+ {
+
+#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED
+ this->exception_state = slp_get_exception_state();
+#endif
+
+ // XXX: Potentially dangerous, exposing a not fully
+ // constructed object.
+ MainGreenlet* const main = this->alloc_main();
+ this->main_greenlet = OwnedMainGreenlet::consuming(
+ main->self()
+ );
+ assert(this->main_greenlet);
+ this->current_greenlet = main->self();
+ // The main greenlet starts with 1 refs: The returned one. We
+ // then copied it to the current greenlet.
+ assert(this->main_greenlet.REFCNT() == 2);
+ }
+
+ 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() const noexcept
+ {
+ return bool(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 noexcept
+ {
+ assert(this->main_greenlet);
+ assert(this->main_greenlet.REFCNT() >= 2);
+ return this->main_greenlet;
+ };
+
+ inline OwnedMainGreenlet get_main_greenlet() const noexcept
+ {
+ 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);
+
+
+
+
+
+}; // namespace greenlet
+
+#endif
diff --git a/blog-app/venv/Lib/site-packages/greenlet/TThreadStateCreator.hpp b/blog-app/venv/Lib/site-packages/greenlet/TThreadStateCreator.hpp
new file mode 100644
index 0000000..2ec7ab5
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/TThreadStateCreator.hpp
@@ -0,0 +1,102 @@
+#ifndef GREENLET_THREAD_STATE_CREATOR_HPP
+#define GREENLET_THREAD_STATE_CREATOR_HPP
+
+#include
+#include
+
+#include "greenlet_internal.hpp"
+#include "greenlet_refs.hpp"
+#include "greenlet_thread_support.hpp"
+
+#include "TThreadState.hpp"
+
+namespace greenlet {
+
+
+typedef void (*ThreadStateDestructor)(ThreadState* const);
+
+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);
+
+ inline bool has_initialized_state() const noexcept
+ {
+ return this->_state != (ThreadState*)1;
+ }
+
+ inline bool has_state() const noexcept
+ {
+ return this->has_initialized_state() && this->_state != nullptr;
+ }
+
+public:
+
+ // Only one of these, auto created per thread.
+ // Constructing the state constructs the MainGreenlet.
+ ThreadStateCreator() :
+ _state((ThreadState*)1)
+ {
+ }
+
+ ~ThreadStateCreator()
+ {
+ if (this->has_state()) {
+ Destructor(this->_state);
+ }
+
+ this->_state = nullptr;
+ }
+
+ 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->has_initialized_state()) {
+ // 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->has_state()) {
+ return this->_state->tp_traverse(visit, arg);
+ }
+ return 0;
+ }
+
+};
+
+
+
+}; // namespace greenlet
+
+#endif
diff --git a/blog-app/venv/Lib/site-packages/greenlet/TThreadStateDestroy.cpp b/blog-app/venv/Lib/site-packages/greenlet/TThreadStateDestroy.cpp
new file mode 100644
index 0000000..449b788
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/TThreadStateDestroy.cpp
@@ -0,0 +1,217 @@
+/* -*- 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 "TGreenlet.hpp"
+
+#include "greenlet_thread_support.hpp"
+#include "greenlet_compiler_compat.hpp"
+#include "TGreenletGlobals.cpp"
+#include "TThreadState.hpp"
+#include "TThreadStateCreator.hpp"
+
+namespace greenlet {
+
+extern "C" {
+
+struct ThreadState_DestroyNoGIL
+{
+ /**
+ This function uses the same lock that the PendingCallback does
+ */
+ static void
+ MarkGreenletDeadAndQueueCleanup(ThreadState* const state)
+ {
+#if GREENLET_BROKEN_THREAD_LOCAL_CLEANUP_JUST_LEAK
+ return;
+#endif
+ // 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 (!MarkGreenletDeadIfNeeded(state)) {
+ // No state, or no greenlet
+ return;
+ }
+
+ // XXX: 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;
+ }
+
+ AddToCleanupQueue(state);
+
+ }
+
+private:
+
+ // If the state has an allocated main greenlet:
+ // - mark the greenlet as dead by disassociating it from the state;
+ // - return 1
+ // Otherwise, return 0.
+ static bool
+ MarkGreenletDeadIfNeeded(ThreadState* const state)
+ {
+ 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().borrow());
+ assert(p->pimpl->thread_state() == state || p->pimpl->thread_state() == nullptr);
+ dynamic_cast(p->pimpl)->thread_state(nullptr);
+ return true;
+ }
+ return false;
+ }
+
+ static void
+ AddToCleanupQueue(ThreadState* const state)
+ {
+ assert(state && state->has_main_greenlet());
+
+ // 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 happened 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);
+
+ 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.
+
+ // A size greater than 1 means that we have already added the pending call,
+ // and in fact, it may be executing now.
+ // If it is executing, our lock makes sure that it will see the item we just added
+ // to the queue on its next iteration (after we release the lock)
+ //
+ // A size of 1 means there is no pending call, OR the pending call is
+ // currently executing, has dropped the lock, and is deleting the last item
+ // from the queue; its next iteration will go ahead and delete the item we just added.
+ // And the pending call we schedule here will have no work to do.
+ int result = AddPendingCall(
+ PendingCallback_DestroyQueueWithGIL,
+ nullptr);
+ 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
+ PendingCallback_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();
+ }
+ assert(to_destroy);
+ assert(to_destroy->has_main_greenlet());
+ // Drop the lock while we do the actual deletion.
+ // This allows other calls to MarkGreenletDeadAndQueueCleanup
+ // to enter and add to our queue.
+ DestroyOneWithGIL(to_destroy);
+ }
+ return 0;
+ }
+
+ static void
+ DestroyOneWithGIL(const ThreadState* const 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.
+ dynamic_cast(main->pimpl)->thread_state(nullptr);
+
+ delete state; // Deleting this runs the destructor, DECREFs the main greenlet.
+ }
+
+
+ static int AddPendingCall(int (*func)(void*), void* arg)
+ {
+ // If the interpreter is in the middle of finalizing, we can't add a
+ // pending call. Trying to do so will end up in a SIGSEGV, as
+ // Py_AddPendingCall will not be able to get the interpreter and will
+ // try to dereference a NULL pointer. It's possible this can still
+ // segfault if we happen to get context switched, and maybe we should
+ // just always implement our own AddPendingCall, but I'd like to see if
+ // this works first
+#if GREENLET_PY313
+ if (Py_IsFinalizing()) {
+#else
+ if (_Py_IsFinalizing()) {
+#endif
+#ifdef GREENLET_DEBUG
+ // No need to log in the general case. Yes, we'll leak,
+ // but we're shutting down so it should be ok.
+ fprintf(stderr,
+ "greenlet: WARNING: Interpreter is finalizing. Ignoring "
+ "call to Py_AddPendingCall; \n");
+#endif
+ return 0;
+ }
+ return Py_AddPendingCall(func, arg);
+ }
+
+
+
+
+
+};
+};
+
+}; // 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/blog-app/venv/Lib/site-packages/greenlet/TUserGreenlet.cpp b/blog-app/venv/Lib/site-packages/greenlet/TUserGreenlet.cpp
new file mode 100644
index 0000000..73a8133
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/TUserGreenlet.cpp
@@ -0,0 +1,662 @@
+/* -*- 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"
+*/
+#ifndef T_USER_GREENLET_CPP
+#define T_USER_GREENLET_CPP
+
+#include "greenlet_internal.hpp"
+#include "TGreenlet.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)
+{
+}
+
+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();
+}
+
+
+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. Some obscure
+ // platforms don't properly support this (old 32-bit Arm, see see
+ // https://github.com/python-greenlet/greenlet/issues/385); that's not
+ // great, but should usually be OK because, as mentioned above, we're
+ // terminating anyway.
+ //
+ // 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() == BorrowedGreenlet(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
+#endif
diff --git a/blog-app/venv/Lib/site-packages/greenlet/__init__.py b/blog-app/venv/Lib/site-packages/greenlet/__init__.py
new file mode 100644
index 0000000..9483b7c
--- /dev/null
+++ b/blog-app/venv/Lib/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.2.2'
+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/blog-app/venv/Lib/site-packages/greenlet/__pycache__/__init__.cpython-310.pyc b/blog-app/venv/Lib/site-packages/greenlet/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..11c4be4
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/greenlet/__pycache__/__init__.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/greenlet/_greenlet.cp310-win_amd64.pyd b/blog-app/venv/Lib/site-packages/greenlet/_greenlet.cp310-win_amd64.pyd
new file mode 100644
index 0000000..53a5aec
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/greenlet/_greenlet.cp310-win_amd64.pyd differ
diff --git a/blog-app/venv/Lib/site-packages/greenlet/greenlet.cpp b/blog-app/venv/Lib/site-packages/greenlet/greenlet.cpp
new file mode 100644
index 0000000..e8d92a0
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/greenlet.cpp
@@ -0,0 +1,320 @@
+/* -*- 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_support.hpp"
+#include "TGreenlet.hpp"
+
+#include "TGreenletGlobals.cpp"
+
+#include "TGreenlet.cpp"
+#include "TMainGreenlet.cpp"
+#include "TUserGreenlet.cpp"
+#include "TBrokenGreenlet.cpp"
+#include "TExceptionState.cpp"
+#include "TPythonState.cpp"
+#include "TStackState.cpp"
+
+#include "TThreadState.hpp"
+#include "TThreadStateCreator.hpp"
+#include "TThreadStateDestroy.cpp"
+
+#include "PyGreenlet.cpp"
+#include "PyGreenletUnswitchable.cpp"
+#include "CObjects.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.
+
+ ***********************************************************/
+
+
+
+
+/***********************************************************/
+
+/* 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();
+}
+}
+
+
+/***********************************************************/
+
+
+#include "PyModule.cpp"
+
+
+
+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/blog-app/venv/Lib/site-packages/greenlet/greenlet.h b/blog-app/venv/Lib/site-packages/greenlet/greenlet.h
new file mode 100644
index 0000000..d02a16e
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/greenlet_allocator.hpp b/blog-app/venv/Lib/site-packages/greenlet/greenlet_allocator.hpp
new file mode 100644
index 0000000..b452f54
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/greenlet_compiler_compat.hpp b/blog-app/venv/Lib/site-packages/greenlet/greenlet_compiler_compat.hpp
new file mode 100644
index 0000000..af24bd8
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/greenlet_compiler_compat.hpp
@@ -0,0 +1,98 @@
+/* -*- 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
+
+# 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
+
+#if defined(__GNUC__) && defined(__POWERPC__) && defined(__APPLE__)
+// 32-bit PPC/MacOSX. Only known to be tested on unreleased versions
+// of macOS 10.6 using a macports build gcc 14. It appears that
+// running C++ destructors of thread-local variables is broken.
+
+// See https://github.com/python-greenlet/greenlet/pull/419
+# define GREENLET_BROKEN_THREAD_LOCAL_CLEANUP_JUST_LEAK 1
+#else
+# define GREENLET_BROKEN_THREAD_LOCAL_CLEANUP_JUST_LEAK 0
+#endif
+
+
+#endif
diff --git a/blog-app/venv/Lib/site-packages/greenlet/greenlet_cpython_compat.hpp b/blog-app/venv/Lib/site-packages/greenlet/greenlet_cpython_compat.hpp
new file mode 100644
index 0000000..979d6f9
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/greenlet_cpython_compat.hpp
@@ -0,0 +1,148 @@
+/* -*- 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
+#else
+# define GREENLET_PY310 0
+#endif
+
+/*
+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.
+
+Python 3.13 removed PyThreadState.cframe (GH-108035).
+*/
+#if GREENLET_PY310 && PY_VERSION_HEX < 0x30D0000
+# define GREENLET_USE_CFRAME 1
+#else
+# define GREENLET_USE_CFRAME 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
+
+#if PY_VERSION_HEX >= 0x30D0000
+# define GREENLET_PY313 1
+#else
+# define GREENLET_PY313 0
+#endif
+
+#if PY_VERSION_HEX >= 0x30E0000
+# define GREENLET_PY314 1
+#else
+# define GREENLET_PY314 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
+
+#if !defined(Py_C_RECURSION_LIMIT) && defined(C_RECURSION_LIMIT)
+# define Py_C_RECURSION_LIMIT C_RECURSION_LIMIT
+#endif
+
+#endif /* GREENLET_CPYTHON_COMPAT_H */
diff --git a/blog-app/venv/Lib/site-packages/greenlet/greenlet_exceptions.hpp b/blog-app/venv/Lib/site-packages/greenlet/greenlet_exceptions.hpp
new file mode 100644
index 0000000..617f07c
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/greenlet_exceptions.hpp
@@ -0,0 +1,171 @@
+#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());
+ }
+
+ PyErrOccurred(PyObject* exc_kind,
+ const std::string msg, //This is the format
+ //string; that's not
+ //usually safe!
+
+ PyObject* borrowed_obj_one, PyObject* borrowed_obj_two)
+ : std::runtime_error(msg)
+ {
+
+ //This is designed specifically for the
+ //``check_switch_allowed`` function.
+
+ // PyObject_Str and PyObject_Repr are safe to call with
+ // NULL pointers; they return the string "" in that
+ // case.
+ // This function always returns null.
+ PyErr_Format(exc_kind,
+ msg.c_str(),
+ borrowed_obj_one, borrowed_obj_two);
+ }
+ };
+
+ 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/blog-app/venv/Lib/site-packages/greenlet/greenlet_internal.hpp b/blog-app/venv/Lib/site-packages/greenlet/greenlet_internal.hpp
new file mode 100644
index 0000000..f2b15d5
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/greenlet_internal.hpp
@@ -0,0 +1,107 @@
+/* -*- 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"
+#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 "TGreenlet.hpp"
+#include "greenlet_allocator.hpp"
+
+#include
+#include
+
+#define GREENLET_MODULE
+struct _greenlet;
+typedef struct _greenlet PyGreenlet;
+namespace greenlet {
+
+ class ThreadState;
+ // 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;
+
+};
+
+
+#define implementation_ptr_t greenlet::Greenlet*
+
+
+#include "greenlet.h"
+
+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 = static_cast(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 PyObject* green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs);
+
+
+#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/blog-app/venv/Lib/site-packages/greenlet/greenlet_refs.hpp b/blog-app/venv/Lib/site-packages/greenlet/greenlet_refs.hpp
new file mode 100644
index 0000000..b7e5e3f
--- /dev/null
+++ b/blog-app/venv/Lib/site-packages/greenlet/greenlet_refs.hpp
@@ -0,0 +1,1118 @@
+#ifndef GREENLET_REFS_HPP
+#define GREENLET_REFS_HPP
+
+#define PY_SSIZE_T_CLEAN
+#include
+
+#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*);
+
+ void
+ NoOpChecker(void*)
+ {
+ return;
+ }
+
+ 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);
+ }
+ }
+
+ 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;
+
+ 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``.
+
+ // XXX: The following two paragraphs do not hold for all platforms.
+ // Notably, 32-bit PPC Linux passes structs by reference, not by
+ // value, so this actually doesn't work. (Although that's the only
+ // platform that doesn't work on.) DO NOT ATTEMPT IT. The
+ // unfortunate consequence of that is that the slots which we
+ // *know* are already type safe will wind up calling the type
+ // checker function (when we had the slots accepting
+ // BorrowedGreenlet, this was bypassed), so this slows us down.
+ // TODO: Optimize this again.
+
+ // 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:
+ 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.
+
+ T* borrow() const noexcept
+ {
+ return this->p;
+ }
+
+ PyObject* borrow_o() const noexcept
+ {
+ return reinterpret_cast(this->p);
+ }
+
+ T* operator->() const noexcept
+ {
+ return this->p;
+ }
+
+ bool is_None() const noexcept
+ {
+ return this->p == Py_None;
+ }
+
+ 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 this->p != nullptr;
+ }
+
+ bool operator!() const noexcept
+ {
+ return this->p == nullptr;
+ }
+
+ Py_ssize_t REFCNT() const noexcept
+ {
+ return p ? Py_REFCNT(p) : -42;
+ }
+
+ 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 PyObject* const rhs) noexcept
+ {
+ return static_cast(lhs.borrow_o()) == static_cast(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);
+ }
+ Greenlet* operator->() const noexcept;
+ 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()));
+ }
+
+ 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/blog-app/venv/Lib/site-packages/greenlet/greenlet_slp_switch.hpp b/blog-app/venv/Lib/site-packages/greenlet/greenlet_slp_switch.hpp
new file mode 100644
index 0000000..bd4b7ae
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/greenlet_thread_support.hpp b/blog-app/venv/Lib/site-packages/greenlet/greenlet_thread_support.hpp
new file mode 100644
index 0000000..3ded7d2
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/__init__.py b/blog-app/venv/Lib/site-packages/greenlet/platform/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/blog-app/venv/Lib/site-packages/greenlet/platform/__pycache__/__init__.cpython-310.pyc b/blog-app/venv/Lib/site-packages/greenlet/platform/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..3054fd3
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/greenlet/platform/__pycache__/__init__.cpython-310.pyc differ
diff --git a/blog-app/venv/Lib/site-packages/greenlet/platform/setup_switch_x64_masm.cmd b/blog-app/venv/Lib/site-packages/greenlet/platform/setup_switch_x64_masm.cmd
new file mode 100644
index 0000000..038ced2
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_aarch64_gcc.h b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_aarch64_gcc.h
new file mode 100644
index 0000000..058617c
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_alpha_unix.h b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_alpha_unix.h
new file mode 100644
index 0000000..7e07abf
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_amd64_unix.h b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_amd64_unix.h
new file mode 100644
index 0000000..d470110
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_arm32_gcc.h b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_arm32_gcc.h
new file mode 100644
index 0000000..655003a
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_arm32_ios.h b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_arm32_ios.h
new file mode 100644
index 0000000..9e640e1
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.asm b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.asm
new file mode 100644
index 0000000..29f9c22
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.obj b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.obj
new file mode 100644
index 0000000..f6f220e
Binary files /dev/null and b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.obj differ
diff --git a/blog-app/venv/Lib/site-packages/greenlet/platform/switch_arm64_msvc.h b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_arm64_msvc.h
new file mode 100644
index 0000000..7ab7f45
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_csky_gcc.h b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_csky_gcc.h
new file mode 100644
index 0000000..ac469d3
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_loongarch64_linux.h b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_loongarch64_linux.h
new file mode 100644
index 0000000..9eaf34e
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_m68k_gcc.h b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_m68k_gcc.h
new file mode 100644
index 0000000..da761c2
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_mips_unix.h b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_mips_unix.h
new file mode 100644
index 0000000..b9003e9
--- /dev/null
+++ b/blog-app/venv/Lib/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/blog-app/venv/Lib/site-packages/greenlet/platform/switch_ppc64_aix.h b/blog-app/venv/Lib/site-packages/greenlet/platform/switch_ppc64_aix.h
new file mode 100644
index 0000000..e7e0b87
--- /dev/null
+++ b/blog-app/venv/Lib/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