pax_global_header00006660000000000000000000000064140146002340014504gustar00rootroot0000000000000052 comment=9bab000978e7ad0a6153afc4fad3fd8eaa4a00ad aiosqlite-0.17.0/000077500000000000000000000000001401460023400135635ustar00rootroot00000000000000aiosqlite-0.17.0/.github/000077500000000000000000000000001401460023400151235ustar00rootroot00000000000000aiosqlite-0.17.0/.github/issue_template.md000066400000000000000000000002261401460023400204700ustar00rootroot00000000000000### Description ### Details * OS: * Python version: * aiosqlite version: * Can you repro on 'main' branch? * Can you repro in a clean virtualenv? aiosqlite-0.17.0/.github/pull_request_template.md000066400000000000000000000000751401460023400220660ustar00rootroot00000000000000### Description Fixes: # aiosqlite-0.17.0/.github/workflows/000077500000000000000000000000001401460023400171605ustar00rootroot00000000000000aiosqlite-0.17.0/.github/workflows/build.yml000066400000000000000000000016521401460023400210060ustar00rootroot00000000000000name: Build on: push: branches: - main tags: - v* pull_request: jobs: aiosqlite: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: python-version: ["3.6", "3.7", "3.8", "3.9"] os: [macOS-latest, ubuntu-latest, windows-latest] steps: - name: Checkout uses: actions/checkout@v1 - name: Set Up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install run: | python -m pip install --upgrade pip make setup pip install -U . - name: Test run: make test - name: Lint run: make lint if: ${{ matrix.python-version != '3.9' }} - name: Coverage run: codecov --token ${{ secrets.CODECOV_TOKEN }} --branch ${{ github.ref }} continue-on-error: true aiosqlite-0.17.0/.gitignore000066400000000000000000000022601401460023400155530ustar00rootroot00000000000000README test.db* html/ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # dotenv .env # virtualenv .venv venv/ ENV/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ # Editors .vscode/ aiosqlite-0.17.0/.pylint000066400000000000000000000403611401460023400151070ustar00rootroot00000000000000[MASTER] # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code extension-pkg-whitelist= # Add files or directories to the blacklist. They should be base names, not # paths. ignore=CVS # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. ignore-patterns= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Use multiple processes to speed up Pylint. jobs=1 # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. load-plugins= # Pickle collected data for later comparisons. persistent=yes # Specify a configuration file. #rcfile= # When enabled, pylint would attempt to guess common misconfiguration and emit # user-friendly hints instead of false-positive error messages suggestion-mode=yes # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED confidence= # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once).You can also use "--disable=all" to # disable everything first and then reenable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" disable=print-statement, parameter-unpacking, unpacking-in-except, old-raise-syntax, backtick, long-suffix, old-ne-operator, old-octal-literal, import-star-module-level, non-ascii-bytes-literal, raw-checker-failed, bad-inline-option, locally-disabled, locally-enabled, file-ignored, suppressed-message, useless-suppression, deprecated-pragma, apply-builtin, basestring-builtin, buffer-builtin, cmp-builtin, coerce-builtin, execfile-builtin, file-builtin, long-builtin, raw_input-builtin, reduce-builtin, standarderror-builtin, unicode-builtin, xrange-builtin, coerce-method, delslice-method, getslice-method, setslice-method, no-absolute-import, old-division, dict-iter-method, dict-view-method, next-method-called, metaclass-assignment, indexing-exception, raising-string, reload-builtin, oct-method, hex-method, nonzero-method, cmp-method, input-builtin, round-builtin, intern-builtin, unichr-builtin, map-builtin-not-iterating, zip-builtin-not-iterating, range-builtin-not-iterating, filter-builtin-not-iterating, using-cmp-argument, eq-without-hash, div-method, idiv-method, rdiv-method, exception-message-attribute, invalid-str-codec, sys-max-int, bad-python3-import, deprecated-string-function, deprecated-str-translate-call, deprecated-itertools-function, deprecated-types-field, next-method-defined, dict-items-not-iterating, dict-keys-not-iterating, dict-values-not-iterating, bad-continuation, missing-docstring, invalid-name, protected-access, wrong-import-order, broad-except, unexpected-line-ending-format, too-many-public-methods, duplicate-bases, cyclic-import, no-else-return # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. enable=c-extension-no-member [REPORTS] # Python expression which should return a note less than 10 (10 is the highest # note). You have access to the variables errors warning, statement which # respectively contain the number of errors / warnings messages and the total # number of statements analyzed. This is used by the global evaluation report # (RP0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details #msg-template= # Set the output format. Available formats are text, parseable, colorized, json # and msvs (visual studio).You can also give a reporter class, eg # mypackage.mymodule.MyReporterClass. output-format=text # Tells whether to display a full report or only the messages reports=no # Activate the evaluation score. score=no [REFACTORING] # Maximum number of nested blocks for function / method body max-nested-blocks=5 # Complete name of functions that never returns. When checking for # inconsistent-return-statements if a never returning function is called then # it will be considered as an explicit return statement and no message will be # printed. never-returning-functions=optparse.Values,sys.exit [LOGGING] # Logging modules to check that the string format arguments are in logging # function parameter format logging-modules=logging [SPELLING] # Limits count of emitted suggestions for spelling mistakes max-spelling-suggestions=4 # Spelling dictionary name. Available dictionaries: none. To make it working # install python-enchant package. spelling-dict= # List of comma separated words that should not be checked. spelling-ignore-words= # A path to a file that contains private dictionary; one word per line. spelling-private-dict-file= # Tells whether to store unknown words to indicated private dictionary in # --spelling-private-dict-file option instead of raising a message. spelling-store-unknown-words=no [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME, XXX, TODO [TYPECHECK] # List of decorators that produce context managers, such as # contextlib.contextmanager. Add to this list to register other decorators that # produce valid context managers. contextmanager-decorators=contextlib.contextmanager # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. generated-members= # Tells whether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=yes # This flag controls whether pylint should warn about no-member and similar # checks whenever an opaque object is returned when inferring. The inference # can return multiple potential results while evaluating a Python object, but # some branches might not be evaluated, which results in partial inference. In # that case, it might be useful to still emit no-member and other checks for # the rest of the inferred objects. ignore-on-opaque-inference=yes # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of # qualified names. ignored-classes=optparse.Values,thread._local,_thread._local # List of module names for which member attributes should not be checked # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis. It # supports qualified module names, as well as Unix pattern matching. ignored-modules= # Show a hint with possible names when a member name was not found. The aspect # of finding the hint is based on edit distance. missing-member-hint=yes # The minimum edit distance a name should have in order to be considered a # similar match for a missing member name. missing-member-hint-distance=1 # The total number of similar names that should be taken in consideration when # showing a hint for a missing member. missing-member-max-choices=1 [VARIABLES] # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. additional-builtins= # Tells whether unused global variables should be treated as a violation. allow-global-unused-variables=yes # List of strings which can identify a callback function by name. A callback # name must start or end with one of those strings. callbacks=cb_, _cb # A regular expression matching the name of dummy variables (i.e. expectedly # not used). dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ # Argument names that match this expression will be ignored. Default to name # with leading underscore ignored-argument-names=_.*|^ignored_|^unused_ # Tells whether we should check for unused import in __init__ files. init-import=no # List of qualified module names which can have objects that can redefine # builtins. redefining-builtins-modules=six.moves,past.builtins,future.builtins [FORMAT] # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. expected-line-ending-format=LF # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ # Number of spaces of indent required inside a hanging or continued line. indent-after-paren=4 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # Maximum number of characters on a single line. max-line-length=88 # Maximum number of lines in a module max-module-lines=1000 # List of optional constructs for which whitespace checking is disabled. `dict- # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. # `trailing-comma` allows a space between comma and closing bracket: (a, ). # `empty-line` allows space-only lines. no-space-check=trailing-comma, dict-separator # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no # Allow the body of an if to be on the same line as the test if there is no # else. single-line-if-stmt=no [SIMILARITIES] # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes # Ignore imports when computing similarities. ignore-imports=no # Minimum lines number of a similarity. min-similarity-lines=4 [BASIC] # Naming style matching correct argument names argument-naming-style=snake_case # Regular expression matching correct argument names. Overrides argument- # naming-style #argument-rgx= # Naming style matching correct attribute names attr-naming-style=snake_case # Regular expression matching correct attribute names. Overrides attr-naming- # style #attr-rgx= # Bad variable names which should always be refused, separated by a comma bad-names=foo, bar, baz, toto, tutu, tata # Naming style matching correct class attribute names class-attribute-naming-style=any # Regular expression matching correct class attribute names. Overrides class- # attribute-naming-style #class-attribute-rgx= # Naming style matching correct class names class-naming-style=PascalCase # Regular expression matching correct class names. Overrides class-naming-style #class-rgx= # Naming style matching correct constant names const-naming-style=UPPER_CASE # Regular expression matching correct constant names. Overrides const-naming- # style #const-rgx= # Minimum line length for functions/classes that require docstrings, shorter # ones are exempt. docstring-min-length=-1 # Naming style matching correct function names function-naming-style=snake_case # Regular expression matching correct function names. Overrides function- # naming-style #function-rgx= # Good variable names which should always be accepted, separated by a comma good-names=i, j, k, ex, Run, _ # Include a hint for the correct naming format with invalid-name include-naming-hint=no # Naming style matching correct inline iteration names inlinevar-naming-style=any # Regular expression matching correct inline iteration names. Overrides # inlinevar-naming-style #inlinevar-rgx= # Naming style matching correct method names method-naming-style=snake_case # Regular expression matching correct method names. Overrides method-naming- # style #method-rgx= # Naming style matching correct module names module-naming-style=snake_case # Regular expression matching correct module names. Overrides module-naming- # style #module-rgx= # Colon-delimited sets of names that determine each other's naming style when # the name regexes allow several styles. name-group= # Regular expression which should only match function or class names that do # not require a docstring. no-docstring-rgx=^_ # List of decorators that produce properties, such as abc.abstractproperty. Add # to this list to register other decorators that produce valid properties. property-classes=abc.abstractproperty # Naming style matching correct variable names variable-naming-style=snake_case # Regular expression matching correct variable names. Overrides variable- # naming-style #variable-rgx= [IMPORTS] # Allow wildcard imports from modules that define __all__. allow-wildcard-with-all=no # Analyse import fallback blocks. This can be used to support both Python 2 and # 3 compatible code, which means that the block might have code that exists # only in one or another interpreter, leading to false positives when analysed. analyse-fallback-blocks=no # Deprecated modules which should not be used, separated by a comma deprecated-modules=optparse,tkinter.tix # Create a graph of external dependencies in the given file (report RP0402 must # not be disabled) ext-import-graph= # Create a graph of every (i.e. internal and external) dependencies in the # given file (report RP0402 must not be disabled) import-graph= # Create a graph of internal dependencies in the given file (report RP0402 must # not be disabled) int-import-graph= # Force import order to recognize a module as part of the standard # compatibility libraries. known-standard-library= # Force import order to recognize a module as part of a third party library. known-third-party=enchant [CLASSES] # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__, __new__, setUp # List of member names, which should be excluded from the protected access # warning. exclude-protected=_asdict, _fields, _replace, _source, _make # List of valid names for the first argument in a class method. valid-classmethod-first-arg=cls # List of valid names for the first argument in a metaclass class method. valid-metaclass-classmethod-first-arg=mcs [DESIGN] # Maximum number of arguments for function / method max-args=5 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Maximum number of boolean expressions in a if statement max-bool-expr=5 # Maximum number of branch for function / method body max-branches=12 # Maximum number of locals for function / method body max-locals=15 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of public methods for a class (see R0904). max-public-methods=21 # Maximum number of return / yield for function / method body max-returns=6 # Maximum number of statements in function / method body max-statements=50 # Minimum number of public methods for a class (see R0903). min-public-methods=2 [EXCEPTIONS] # Exceptions that will emit a warning when being caught. Defaults to # "Exception" overgeneral-exceptions=Exception aiosqlite-0.17.0/.pyup.yml000066400000000000000000000001141401460023400153550ustar00rootroot00000000000000assignees: jreese label_prs: update schedule: "every two weeks on saturday" aiosqlite-0.17.0/.readthedocs.yml000066400000000000000000000002341401460023400166500ustar00rootroot00000000000000version: 2 sphinx: configuration: docs/conf.py python: version: 3.7 install: - requirements: requirements-dev.txt - method: pip path: . aiosqlite-0.17.0/CHANGELOG.md000066400000000000000000000125101401460023400153730ustar00rootroot00000000000000aiosqlite ========= v0.17.0 ------- Feature release * Connection objects now raise ValueError when closed and a command is executed (#79) * Fix documented examples in readme (#104) ``` $ git shortlog -s v0.16.1...v0.17.0 3 John Reese 5 Mariano Sorgente 1 Nuno André 1 pyup.io bot ``` v0.16.1 ------- Bug fix release - Reduce logging severity for exceptions (#93) - Stop logging result objects; they can be big (#102) ``` $ git shortlog -s v0.16.0...v0.16.1 1 Alexei Chetroi 3 John Reese 3 pyup.io bot ``` v0.16.0 ------- Feature release * Improved performance for async iteration on cursors (#34, #86) * Support for deterministic user functions in Python 3.8+ (#81, #83, #84) * Reduced logging severity for exceptions returned from children (#75, #76) * Fix InvalidStateError when setting future results (#80, #89) * Allow user to catch exceptions from `close()` (#68, #90) * Tested under Python 3.9 (#91) ``` $ git shortlog -s v0.15.0...v0.16.0 3 Caleb Hattingh 1 Groosha 14 John Reese 1 Lonami 4 Lonami Exo 4 ZsoltM 1 pyup.io bot ``` v0.15.0 ------- Feature release - Support for accessing connections from multiple event loops - Fixed type annotations for connection methods returning cursors - Move cursors into separate module from connections - Deprecated `loop` parameter to `connect()` and `Connection` ``` $ git shortlog -s v0.14.1...v0.15.0 7 John Reese ``` v0.14.1 ------- Bugfix release - Remove debugging print() calls. Oops! (#72) ``` $ git shortlog -s v0.14.0...v0.14.1 2 John Reese 1 Spyros Roum ``` v0.14.0 ------- Feature release - `Connection.backup()` now supported (#71) - PEP 561 support added to mark the package as type annotated (#69) - Better/fixed type annotations for context managers (#70) ``` $ git shortlog -s v0.13.0...v0.14.0 5 John Reese 3 montag451 ``` v0.13.0 ------- Feature release - `cursor.execute*()` now returns the cursor to match sqlite3 API (#62) - `Connection.set_trace_callback()` now supported (#62) - `Connection.iterdump()` is now supported (#66) - Fixed possible hung thread if connection failed (#55) - Dropped support for Python 3.5 ``` $ git shortlog -s v0.12.0...v0.13.0 32 John Reese 1 pyup.io bot 5 shipmints ``` v0.12.0 ------- Feature Release - Add support for custom functions (#58) - Official support for Python 3.8 ``` $ git shortlog -s v0.11.0...v0.12.0 4 John Reese 1 dmitrypolo 3 pyup.io bot ``` v0.11.0 ------- Feature release v0.11.0 - Added support for `set_progress_handler` (#49) - Improved and updated documentation ``` $ git shortlog -s v0.10.0...v0.11.0 11 John Reese 4 Stanislas 2 Vladislav Yarmak 1 pyup-bot 5 tat2grl85 ``` v0.10.0 ------- Feature release v0.10.0: - Support using connections without context managers (#29) - Include test suite in aiosqlite package ``` $ git shortlog -s v0.9.0...v0.10.0 16 John Reese 1 Simon Willison 1 dark0ghost ``` v0.9.0 ------ Feature release v0.9.0: - Support for sqlite extensions - Fixed support for type annotations on early Python 3.5 ``` $ git shortlog -s v0.8.1...v0.9.0 2 Alexander Lyon 3 John Reese ``` v0.8.1 ------ Bug fix release v0.8.1: - Fix connections to byte string db locations (#20) ``` $ git shortlog -s v0.8.0...v0.8.1 1 DevilXD 6 John Reese ``` v0.8.0 ------ Major release v0.8.0: - Use futures instead of polling for connections/cursors. This will significantly reduce time spent blocking the primary event loop, resulting in better performance of asyncio applications using aiosqlite. ``` $ git shortlog -s v0.7.0...v0.8.0 3 John Reese 2 Matthew Schubert ``` v0.7.0 ------ Feature release v0.7.0: - Added macros for combined insert/id and select/fetch - Better perf testing output ``` $ git shortlog -s v0.6.0...v0.7.0 1 Grigi 4 John Reese ``` v0.6.0 ------ Feature release v0.6.0: - Performance improvements for atomic or fast queries - Support passing Path-like objects to aiosqlite.connect - Unit tests now use aiounittest instead of a custom test harness - Limited set of performance tests now available ``` $ git shortlog -s v0.5.0...v0.6.0 1 Grigi 8 John Reese ``` v0.5.0 ------ Feature release v0.5.0: - More aliases from sqlite3, including Row, errors, and register_* - Additional connection properties for row/text factory, total changes - Better readme ``` $ git shortlog -s v0.4.0...v0.5.0 6 John Reese ``` v0.4.0 ------ Feature release v0.4.0: - Enable using a custom asyncio event loop - Increase performance by decreasing sleep time ``` $ git shortlog -s v0.3.0...v0.4.0 15 John Reese 1 Justin Kula 1 Richard Schwab ``` v0.3.0 ------ Feature release v0.3.0: - Cursors can be used as context managers ``` $ git shortlog -s v0.2.2...v0.3.0 6 John Reese 5 Linus Lewandowski ``` v0.2.2 ------ Minor release: - Correct aiosqlite.__version__ - Markdown readme, release via twine ``` $ git shortlog -s v0.2.1...v0.2.2 5 John Reese ``` v0.2.1 ------ Minor release v0.2.1: - Increase polling speed on event loop - Using black and pylint ``` $ git shortlog -s v0.2.0...v0.2.1 8 John Reese 2 Pavol Vargovcik ``` v0.2.0 ------ Beta version 0.2.0 ``` $ git shortlog -s c606de200b7afe6d73a6e2900557d622c62ed78c...v0.2.0 1 John Reese ``` aiosqlite-0.17.0/CODE_OF_CONDUCT.md000066400000000000000000000062141401460023400163650ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at john@noswap.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ aiosqlite-0.17.0/CONTRIBUTING.md000066400000000000000000000024731401460023400160220ustar00rootroot00000000000000# Contributing to aiosqlite ## Preparation You'll need to have at least Python 3.5 available for testing. Python 3.6 or newer is recommended in order to validate formatting. You can do this with [pyenv][]: $ pyenv install 3.6.10 $ pyenv local 3.6.10 ## Setup Once cloned, create a clean virtual environment and install the appropriate tools and dependencies: $ cd $ make venv $ source .venv/bin/activate ## Formatting aiosqlite uses *[black][]* and [isort][] for formatting code and imports, respectively. If your editor does not already support this workflow, you can manually format files: $ make format ## Testing Once you've made changes, you should run unit tests, validate your type annotations, and ensure your code meets the appropriate style and linting rules: $ make test lint ## Submitting Before submitting a pull request, please ensure that you have done the following: * Documented changes or features in README.md * Added appropriate license headers to new files * Written or modified tests for new functionality * Formatted code following project standards * Validated code and formatting with `make test lint` [black]: https://github.com/psf/black [isort]: https://timothycrosley.github.io/isort/ [pyenv]: https://github.com/pyenv/pyenv aiosqlite-0.17.0/LICENSE000066400000000000000000000020531401460023400145700ustar00rootroot00000000000000MIT License Copyright (c) 2017 John Reese 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. aiosqlite-0.17.0/MANIFEST.in000066400000000000000000000000321401460023400153140ustar00rootroot00000000000000include README.md LICENSE aiosqlite-0.17.0/README.rst000066400000000000000000000066011401460023400152550ustar00rootroot00000000000000aiosqlite\: Sqlite for AsyncIO ============================== .. image:: https://img.shields.io/pypi/v/aiosqlite.svg :target: https://pypi.org/project/aiosqlite :alt: PyPI Release .. image:: https://readthedocs.org/projects/aiosqlite/badge/?version=latest :target: https://aiosqlite.omnilib.dev/en/latest/?badge=latest :alt: Documentation Status .. image:: https://img.shields.io/badge/change-log-blue :target: https://github.com/omnilib/aiosqlite/blob/master/CHANGELOG.md :alt: Changelog .. image:: https://img.shields.io/codecov/c/github/omnilib/aiosqlite/master.svg :target: https://codecov.io/gh/omnilib/aiosqlite :alt: Code Coverage .. image:: https://github.com/omnilib/aiosqlite/workflows/Build/badge.svg :target: https://github.com/omnilib/aiosqlite/actions :alt: Build Status .. image:: https://img.shields.io/pypi/l/aiosqlite.svg :target: https://github.com/omnilib/aiosqlite/blob/master/LICENSE :alt: MIT Licensed .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/ambv/black :alt: Code Style Black aiosqlite provides a friendly, async interface to sqlite databases. It replicates the standard ``sqlite3`` module, but with async versions of all the standard connection and cursor methods, plus context managers for automatically closing connections and cursors:: async with aiosqlite.connect(...) as db: await db.execute("INSERT INTO some_table ...") await db.commit() async with db.execute("SELECT * FROM some_table") as cursor: async for row in cursor: ... It can also be used in the traditional, procedural manner:: db = await aiosqlite.connect(...) cursor = await db.execute('SELECT * FROM some_table') row = await cursor.fetchone() rows = await cursor.fetchall() await cursor.close() await db.close() aiosqlite also replicates most of the advanced features of ``sqlite3``:: async with aiosqlite.connect(...) as db: db.row_factory = aiosqlite.Row async with db.execute('SELECT * FROM some_table') as cursor: async for row in cursor: value = row['column'] await db.execute('INSERT INTO foo some_table') assert db.total_changes > 0 Install ------- aiosqlite is compatible with Python 3.6 and newer. You can install it from PyPI: .. code-block:: bash $ pip install aiosqlite Details ------- aiosqlite allows interaction with SQLite databases on the main AsyncIO event loop without blocking execution of other coroutines while waiting for queries or data fetches. It does this by using a single, shared thread per connection. This thread executes all actions within a shared request queue to prevent overlapping actions. Connection objects are proxies to the real connections, contain the shared execution thread, and provide context managers to handle automatically closing connections. Cursors are similarly proxies to the real cursors, and provide async iterators to query results. License ------- aiosqlite is copyright `John Reese `_, and licensed under the MIT license. I am providing code in this repository to you under an open source license. This is my personal repository; the license you receive to my code is from me and not from my employer. See the `LICENSE`_ file for details. .. _LICENSE: https://github.com/omnilib/aiosqlite/blob/master/LICENSE aiosqlite-0.17.0/aiosqlite/000077500000000000000000000000001401460023400155555ustar00rootroot00000000000000aiosqlite-0.17.0/aiosqlite/__init__.py000066400000000000000000000015231401460023400176670ustar00rootroot00000000000000# Copyright 2018 John Reese # Licensed under the MIT license """asyncio bridge to the standard sqlite3 module""" from sqlite3 import ( # pylint: disable=redefined-builtin DatabaseError, Error, IntegrityError, NotSupportedError, OperationalError, ProgrammingError, Row, Warning, register_adapter, register_converter, sqlite_version, sqlite_version_info, ) __author__ = "John Reese" from .__version__ import __version__ from .core import Connection, Cursor, connect __all__ = [ "__version__", "register_adapter", "register_converter", "sqlite_version", "sqlite_version_info", "connect", "Connection", "Cursor", "Row", "Warning", "Error", "DatabaseError", "IntegrityError", "ProgrammingError", "OperationalError", "NotSupportedError", ] aiosqlite-0.17.0/aiosqlite/__version__.py000066400000000000000000000000271401460023400204070ustar00rootroot00000000000000__version__ = "0.17.0" aiosqlite-0.17.0/aiosqlite/context.py000066400000000000000000000025721401460023400176210ustar00rootroot00000000000000# Copyright 2018 # Licensed under the MIT license from functools import wraps from typing import Any, Callable, Coroutine, Generator, TypeVar from typing_extensions import AsyncContextManager from .cursor import Cursor _T = TypeVar("_T") class Result(AsyncContextManager[_T], Coroutine[Any, Any, _T]): __slots__ = ("_coro", "_obj") def __init__(self, coro: Coroutine[Any, Any, _T]): self._coro = coro self._obj: _T def send(self, value) -> None: return self._coro.send(value) def throw(self, typ, val=None, tb=None) -> None: if val is None: return self._coro.throw(typ) if tb is None: return self._coro.throw(typ, val) return self._coro.throw(typ, val, tb) def close(self) -> None: return self._coro.close() def __await__(self) -> Generator[Any, None, _T]: return self._coro.__await__() async def __aenter__(self) -> _T: self._obj = await self._coro return self._obj async def __aexit__(self, exc_type, exc, tb) -> None: if isinstance(self._obj, Cursor): await self._obj.close() def contextmanager( method: Callable[..., Coroutine[Any, Any, _T]] ) -> Callable[..., Result[_T]]: @wraps(method) def wrapper(self, *args, **kwargs) -> Result[_T]: return Result(method(self, *args, **kwargs)) return wrapper aiosqlite-0.17.0/aiosqlite/core.py000066400000000000000000000303131401460023400170570ustar00rootroot00000000000000# Copyright 2018 John Reese # Licensed under the MIT license """ Core implementation of aiosqlite proxies """ import asyncio import logging import sqlite3 import sys import warnings from functools import partial from pathlib import Path from queue import Empty, Queue from threading import Thread from typing import ( Any, AsyncIterator, Callable, Generator, Iterable, Optional, Type, Union, ) from warnings import warn from .context import contextmanager from .cursor import Cursor __all__ = ["connect", "Connection", "Cursor"] LOG = logging.getLogger("aiosqlite") def get_loop(future: asyncio.Future) -> asyncio.AbstractEventLoop: if sys.version_info >= (3, 7): return future.get_loop() else: return future._loop class Connection(Thread): def __init__( self, connector: Callable[[], sqlite3.Connection], iter_chunk_size: int, loop: Optional[asyncio.AbstractEventLoop] = None, ) -> None: super().__init__() self._running = True self._connection: Optional[sqlite3.Connection] = None self._connector = connector self._tx: Queue = Queue() self._iter_chunk_size = iter_chunk_size if loop is not None: warn( "aiosqlite.Connection no longer uses the `loop` parameter", DeprecationWarning, ) @property def _conn(self) -> sqlite3.Connection: if self._connection is None: raise ValueError("no active connection") return self._connection def _execute_insert( self, sql: str, parameters: Iterable[Any] ) -> Optional[sqlite3.Row]: cursor = self._conn.execute(sql, parameters) cursor.execute("SELECT last_insert_rowid()") return cursor.fetchone() def _execute_fetchall( self, sql: str, parameters: Iterable[Any] ) -> Iterable[sqlite3.Row]: cursor = self._conn.execute(sql, parameters) return cursor.fetchall() def run(self) -> None: """ Execute function calls on a separate thread. :meta private: """ while True: # Continues running until all queue items are processed, # even after connection is closed (so we can finalize all # futures) try: future, function = self._tx.get(timeout=0.1) except Empty: if self._running: continue break try: LOG.debug("executing %s", function) result = function() LOG.debug("operation %s completed", function) def set_result(fut, result): if not fut.done(): fut.set_result(result) get_loop(future).call_soon_threadsafe(set_result, future, result) except BaseException as e: LOG.debug("returning exception %s", e) def set_exception(fut, e): if not fut.done(): fut.set_exception(e) get_loop(future).call_soon_threadsafe(set_exception, future, e) async def _execute(self, fn, *args, **kwargs): """Queue a function with the given arguments for execution.""" if not self._running or not self._connection: raise ValueError("Connection closed") function = partial(fn, *args, **kwargs) future = asyncio.get_event_loop().create_future() self._tx.put_nowait((future, function)) return await future async def _connect(self) -> "Connection": """Connect to the actual sqlite database.""" if self._connection is None: try: future = asyncio.get_event_loop().create_future() self._tx.put_nowait((future, self._connector)) self._connection = await future except Exception: self._running = False self._connection = None raise return self def __await__(self) -> Generator[Any, None, "Connection"]: self.start() return self._connect().__await__() async def __aenter__(self) -> "Connection": return await self async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: await self.close() @contextmanager async def cursor(self) -> Cursor: """Create an aiosqlite cursor wrapping a sqlite3 cursor object.""" return Cursor(self, await self._execute(self._conn.cursor)) async def commit(self) -> None: """Commit the current transaction.""" await self._execute(self._conn.commit) async def rollback(self) -> None: """Roll back the current transaction.""" await self._execute(self._conn.rollback) async def close(self) -> None: """Complete queued queries/cursors and close the connection.""" try: await self._execute(self._conn.close) except Exception: LOG.info("exception occurred while closing connection") raise finally: self._running = False self._connection = None @contextmanager async def execute(self, sql: str, parameters: Iterable[Any] = None) -> Cursor: """Helper to create a cursor and execute the given query.""" if parameters is None: parameters = [] cursor = await self._execute(self._conn.execute, sql, parameters) return Cursor(self, cursor) @contextmanager async def execute_insert( self, sql: str, parameters: Iterable[Any] = None ) -> Optional[sqlite3.Row]: """Helper to insert and get the last_insert_rowid.""" if parameters is None: parameters = [] return await self._execute(self._execute_insert, sql, parameters) @contextmanager async def execute_fetchall( self, sql: str, parameters: Iterable[Any] = None ) -> Iterable[sqlite3.Row]: """Helper to execute a query and return all the data.""" if parameters is None: parameters = [] return await self._execute(self._execute_fetchall, sql, parameters) @contextmanager async def executemany( self, sql: str, parameters: Iterable[Iterable[Any]] ) -> Cursor: """Helper to create a cursor and execute the given multiquery.""" cursor = await self._execute(self._conn.executemany, sql, parameters) return Cursor(self, cursor) @contextmanager async def executescript(self, sql_script: str) -> Cursor: """Helper to create a cursor and execute a user script.""" cursor = await self._execute(self._conn.executescript, sql_script) return Cursor(self, cursor) async def interrupt(self) -> None: """Interrupt pending queries.""" return self._conn.interrupt() async def create_function( self, name: str, num_params: int, func: Callable, deterministic: bool = False ) -> None: """ Create user-defined function that can be later used within SQL statements. Must be run within the same thread that query executions take place so instead of executing directly against the connection, we defer this to `run` function. In Python 3.8 and above, if *deterministic* is true, the created function is marked as deterministic, which allows SQLite to perform additional optimizations. This flag is supported by SQLite 3.8.3 or higher, ``NotSupportedError`` will be raised if used with older versions. """ if sys.version_info >= (3, 8): await self._execute( self._conn.create_function, name, num_params, func, deterministic=deterministic, ) else: if deterministic: warnings.warn( "Deterministic function support is only available on " 'Python 3.8+. Function "{}" will be registered as ' "non-deterministic as per SQLite defaults.".format(name) ) await self._execute(self._conn.create_function, name, num_params, func) @property def in_transaction(self) -> bool: return self._conn.in_transaction @property def isolation_level(self) -> str: return self._conn.isolation_level @isolation_level.setter def isolation_level(self, value: str) -> None: self._conn.isolation_level = value @property def row_factory(self) -> "Optional[Type]": # py3.5.2 compat (#24) return self._conn.row_factory @row_factory.setter def row_factory(self, factory: "Optional[Type]") -> None: # py3.5.2 compat (#24) self._conn.row_factory = factory @property def text_factory(self) -> Type: return self._conn.text_factory @text_factory.setter def text_factory(self, factory: Type) -> None: self._conn.text_factory = factory @property def total_changes(self) -> int: return self._conn.total_changes async def enable_load_extension(self, value: bool) -> None: await self._execute(self._conn.enable_load_extension, value) # type: ignore async def load_extension(self, path: str): await self._execute(self._conn.load_extension, path) # type: ignore async def set_progress_handler( self, handler: Callable[[], Optional[int]], n: int ) -> None: await self._execute(self._conn.set_progress_handler, handler, n) async def set_trace_callback(self, handler: Callable) -> None: await self._execute(self._conn.set_trace_callback, handler) async def iterdump(self) -> AsyncIterator[str]: """ Return an async iterator to dump the database in SQL text format. Example:: async for line in db.iterdump(): ... """ dump_queue: Queue = Queue() def dumper(): try: for line in self._conn.iterdump(): dump_queue.put_nowait(line) dump_queue.put_nowait(None) except Exception: LOG.exception("exception while dumping db") dump_queue.put_nowait(None) raise fut = self._execute(dumper) task = asyncio.ensure_future(fut) while True: try: line: Optional[str] = dump_queue.get_nowait() if line is None: break yield line except Empty: if task.done(): LOG.warning("iterdump completed unexpectedly") break await asyncio.sleep(0.01) await task async def backup( self, target: Union["Connection", sqlite3.Connection], *, pages: int = 0, progress: Optional[Callable[[int, int, int], None]] = None, name: str = "main", sleep: float = 0.250 ) -> None: """ Make a backup of the current database to the target database. Takes either a standard sqlite3 or aiosqlite Connection object as the target. """ if sys.version_info < (3, 7): raise RuntimeError("backup() method is only available on Python 3.7+") if isinstance(target, Connection): target = target._conn await self._execute( self._conn.backup, target, pages=pages, progress=progress, name=name, sleep=sleep, ) def connect( database: Union[str, Path], *, iter_chunk_size=64, loop: Optional[asyncio.AbstractEventLoop] = None, **kwargs: Any ) -> Connection: """Create and return a connection proxy to the sqlite database.""" if loop is not None: warn( "aiosqlite.connect() no longer uses the `loop` parameter", DeprecationWarning, ) def connector() -> sqlite3.Connection: if isinstance(database, str): loc = database elif isinstance(database, bytes): loc = database.decode("utf-8") else: loc = str(database) return sqlite3.connect(loc, **kwargs) return Connection(connector, iter_chunk_size) aiosqlite-0.17.0/aiosqlite/cursor.py000066400000000000000000000060161401460023400174470ustar00rootroot00000000000000# Copyright 2018 John Reese # Licensed under the MIT license import sqlite3 from typing import TYPE_CHECKING, Any, AsyncIterator, Iterable, Optional, Tuple if TYPE_CHECKING: from .core import Connection class Cursor: def __init__(self, conn: "Connection", cursor: sqlite3.Cursor) -> None: self.iter_chunk_size = conn._iter_chunk_size self._conn = conn self._cursor = cursor def __aiter__(self) -> AsyncIterator[sqlite3.Row]: """The cursor proxy is also an async iterator.""" return self._fetch_chunked() async def _fetch_chunked(self): while True: rows = await self.fetchmany(self.iter_chunk_size) if not rows: return for row in rows: yield row async def _execute(self, fn, *args, **kwargs): """Execute the given function on the shared connection's thread.""" return await self._conn._execute(fn, *args, **kwargs) async def execute(self, sql: str, parameters: Iterable[Any] = None) -> "Cursor": """Execute the given query.""" if parameters is None: parameters = [] await self._execute(self._cursor.execute, sql, parameters) return self async def executemany( self, sql: str, parameters: Iterable[Iterable[Any]] ) -> "Cursor": """Execute the given multiquery.""" await self._execute(self._cursor.executemany, sql, parameters) return self async def executescript(self, sql_script: str) -> "Cursor": """Execute a user script.""" await self._execute(self._cursor.executescript, sql_script) return self async def fetchone(self) -> Optional[sqlite3.Row]: """Fetch a single row.""" return await self._execute(self._cursor.fetchone) async def fetchmany(self, size: int = None) -> Iterable[sqlite3.Row]: """Fetch up to `cursor.arraysize` number of rows.""" args: Tuple[int, ...] = () if size is not None: args = (size,) return await self._execute(self._cursor.fetchmany, *args) async def fetchall(self) -> Iterable[sqlite3.Row]: """Fetch all remaining rows.""" return await self._execute(self._cursor.fetchall) async def close(self) -> None: """Close the cursor.""" await self._execute(self._cursor.close) @property def rowcount(self) -> int: return self._cursor.rowcount @property def lastrowid(self) -> int: return self._cursor.lastrowid @property def arraysize(self) -> int: return self._cursor.arraysize @arraysize.setter def arraysize(self, value: int) -> None: self._cursor.arraysize = value @property def description(self) -> Tuple[Tuple]: return self._cursor.description @property def connection(self) -> sqlite3.Connection: return self._cursor.connection async def __aenter__(self): return self async def __aexit__(self, exc_type, exc_val, exc_tb): await self.close() aiosqlite-0.17.0/aiosqlite/py.typed000066400000000000000000000000001401460023400172420ustar00rootroot00000000000000aiosqlite-0.17.0/aiosqlite/tests/000077500000000000000000000000001401460023400167175ustar00rootroot00000000000000aiosqlite-0.17.0/aiosqlite/tests/__init__.py000066400000000000000000000001331401460023400210250ustar00rootroot00000000000000# Copyright 2018 John Reese # Licensed under the MIT license from .smoke import SmokeTest aiosqlite-0.17.0/aiosqlite/tests/__main__.py000066400000000000000000000002431401460023400210100ustar00rootroot00000000000000# Copyright 2018 John Reese # Licensed under the MIT license import unittest if __name__ == "__main__": unittest.main(module="aiosqlite.tests", verbosity=2) aiosqlite-0.17.0/aiosqlite/tests/helpers.py000066400000000000000000000013231401460023400207320ustar00rootroot00000000000000# Copyright 2018 John Reese # Licensed under the MIT license import logging import sys def setup_logger(): log = logging.getLogger("") log.setLevel(logging.INFO) logging.addLevelName(logging.ERROR, "E") logging.addLevelName(logging.WARNING, "W") logging.addLevelName(logging.INFO, "I") logging.addLevelName(logging.DEBUG, "V") date_fmt = r"%H:%M:%S" verbose_fmt = ( "%(asctime)s,%(msecs)d %(levelname)s " "%(module)s:%(funcName)s():%(lineno)d " "%(message)s" ) handler = logging.StreamHandler(sys.stdout) handler.setLevel(logging.INFO) handler.setFormatter(logging.Formatter(verbose_fmt, date_fmt)) log.addHandler(handler) return log aiosqlite-0.17.0/aiosqlite/tests/perf.py000066400000000000000000000132671401460023400202360ustar00rootroot00000000000000# Copyright 2018 John Reese # Licensed under the MIT license """ Simple perf tests for aiosqlite and the asyncio run loop. """ import string import time import aiounittest import aiosqlite from .smoke import setup_logger TEST_DB = ":memory:" TARGET = 2.0 RESULTS = {} def timed(fn, name=None): """ Decorator for perf testing a block of async code. Expects the wrapped function to return an async generator. The generator should do setup, then yield when ready to start perf testing. The decorator will then pump the generator repeatedly until the target time has been reached, then close the generator and print perf results. """ name = name or fn.__name__ async def wrapper(*args, **kwargs): gen = fn(*args, **kwargs) await gen.asend(None) count = 0 before = time.time() while True: count += 1 value = time.time() - before < TARGET try: if value: await gen.asend(value) else: await gen.aclose() break except StopAsyncIteration: break except Exception as e: print(f"exception occurred: {e}") return duration = time.time() - before RESULTS[name] = (count, duration) return wrapper class PerfTest(aiounittest.AsyncTestCase): @classmethod def setUpClass(cls): print(f"Running perf tests for at least {TARGET:.1f}s each...") setup_logger() @classmethod def tearDownClass(cls): print(f"\n{'Perf Test':<25} Iterations Duration {'Rate':>11}") for name in sorted(RESULTS): count, duration = RESULTS[name] rate = count / duration name = name.replace("test_", "") print(f"{name:<25} {count:>10} {duration:>7.1f}s {rate:>9.1f}/s") @timed async def test_atomics(self): async with aiosqlite.connect(TEST_DB) as db: await db.execute("create table perf (i integer primary key asc, k integer)") await db.execute("insert into perf (k) values (2), (3)") await db.commit() while True: yield async with db.execute("select last_insert_rowid()") as cursor: _row_id = await cursor.fetchone() @timed async def test_inserts(self): async with aiosqlite.connect(TEST_DB) as db: await db.execute("create table perf (i integer primary key asc, k integer)") await db.commit() while True: yield await db.execute("insert into perf (k) values (1), (2), (3)") await db.commit() @timed async def test_insert_ids(self): async with aiosqlite.connect(TEST_DB) as db: await db.execute("create table perf (i integer primary key asc, k integer)") await db.commit() while True: yield cursor = await db.execute("insert into perf (k) values (1)") await cursor.execute("select last_insert_rowid()") await cursor.fetchone() await db.commit() @timed async def test_insert_macro_ids(self): async with aiosqlite.connect(TEST_DB) as db: await db.execute("create table perf (i integer primary key asc, k integer)") await db.commit() while True: yield await db.execute_insert("insert into perf (k) values (1)") await db.commit() @timed async def test_select(self): async with aiosqlite.connect(TEST_DB) as db: await db.execute("create table perf (i integer primary key asc, k integer)") for i in range(100): await db.execute("insert into perf (k) values (%d)" % (i,)) await db.commit() while True: yield cursor = await db.execute("select i, k from perf") assert len(await cursor.fetchall()) == 100 @timed async def test_select_macro(self): async with aiosqlite.connect(TEST_DB) as db: await db.execute("create table perf (i integer primary key asc, k integer)") for i in range(100): await db.execute("insert into perf (k) values (%d)" % (i,)) await db.commit() while True: yield assert len(await db.execute_fetchall("select i, k from perf")) == 100 async def test_iterable_cursor_perf(self): async with aiosqlite.connect(TEST_DB) as db: await db.execute( "create table ic_perf (" "i integer primary key asc, k integer, a integer, b integer, c char(16))" ) for batch in range(128): # add 128k rows r_start = batch * 1024 await db.executemany( "insert into ic_perf (k, a, b, c) values(?, 1, 2, ?)", [ *[ (i, string.ascii_lowercase) for i in range(r_start, r_start + 1024) ] ], ) await db.commit() async def test_perf(chunk_size: int): while True: async with db.execute("SELECT * FROM ic_perf") as cursor: cursor.iter_chunk_size = chunk_size async for _ in cursor: yield for chunk_size in [2 ** i for i in range(4, 11)]: await timed(test_perf, f"iterable_cursor @ {chunk_size}")(chunk_size) aiosqlite-0.17.0/aiosqlite/tests/smoke.py000066400000000000000000000420541401460023400204140ustar00rootroot00000000000000# Copyright 2018 John Reese # Licensed under the MIT license import asyncio import sqlite3 import sys from pathlib import Path from sqlite3 import OperationalError from threading import Thread from unittest import SkipTest, skipIf, skipUnless import aiounittest import aiosqlite from .helpers import setup_logger TEST_DB = Path("test.db") # pypy uses non-standard text factory for low-level sqlite implementation try: from _sqlite3 import _unicode_text_factory as default_text_factory except ImportError: default_text_factory = str class SmokeTest(aiounittest.AsyncTestCase): @classmethod def setUpClass(cls): setup_logger() def setUp(self): if TEST_DB.exists(): TEST_DB.unlink() def tearDown(self): if TEST_DB.exists(): TEST_DB.unlink() async def test_connection_await(self): db = await aiosqlite.connect(TEST_DB) self.assertIsInstance(db, aiosqlite.Connection) async with db.execute("select 1, 2") as cursor: rows = await cursor.fetchall() self.assertEqual(rows, [(1, 2)]) await db.close() async def test_connection_context(self): async with aiosqlite.connect(TEST_DB) as db: self.assertIsInstance(db, aiosqlite.Connection) async with db.execute("select 1, 2") as cursor: rows = await cursor.fetchall() self.assertEqual(rows, [(1, 2)]) async def test_connection_locations(self): class Fake: # pylint: disable=too-few-public-methods def __str__(self): return str(TEST_DB) locs = ("test.db", b"test.db", Path("test.db"), Fake()) async with aiosqlite.connect(TEST_DB) as db: await db.execute("create table foo (i integer, k integer)") await db.execute("insert into foo (i, k) values (1, 5)") await db.commit() cursor = await db.execute("select * from foo") rows = await cursor.fetchall() for loc in locs: async with aiosqlite.connect(loc) as db: cursor = await db.execute("select * from foo") self.assertEqual(await cursor.fetchall(), rows) async def test_multiple_connections(self): async with aiosqlite.connect(TEST_DB) as db: await db.execute( "create table multiple_connections " "(i integer primary key asc, k integer)" ) async def do_one_conn(i): async with aiosqlite.connect(TEST_DB) as db: await db.execute("insert into multiple_connections (k) values (?)", [i]) await db.commit() await asyncio.gather(*[do_one_conn(i) for i in range(10)]) async with aiosqlite.connect(TEST_DB) as db: cursor = await db.execute("select * from multiple_connections") rows = await cursor.fetchall() assert len(rows) == 10 async def test_multiple_queries(self): async with aiosqlite.connect(TEST_DB) as db: await db.execute( "create table multiple_queries " "(i integer primary key asc, k integer)" ) await asyncio.gather( *[ db.execute("insert into multiple_queries (k) values (?)", [i]) for i in range(10) ] ) await db.commit() async with aiosqlite.connect(TEST_DB) as db: cursor = await db.execute("select * from multiple_queries") rows = await cursor.fetchall() assert len(rows) == 10 async def test_iterable_cursor(self): async with aiosqlite.connect(TEST_DB) as db: cursor = await db.cursor() await cursor.execute( "create table iterable_cursor " "(i integer primary key asc, k integer)" ) await cursor.executemany( "insert into iterable_cursor (k) values (?)", [[i] for i in range(10)] ) await db.commit() async with aiosqlite.connect(TEST_DB) as db: cursor = await db.execute("select * from iterable_cursor") rows = [] async for row in cursor: rows.append(row) assert len(rows) == 10 async def test_multi_loop_usage(self): results = {} def runner(k, conn): async def query(): async with conn.execute("select * from foo") as cursor: rows = await cursor.fetchall() self.assertEqual(len(rows), 2) return rows with self.subTest(k): loop = asyncio.new_event_loop() rows = loop.run_until_complete(query()) loop.close() results[k] = rows async with aiosqlite.connect(":memory:") as db: await db.execute("create table foo (id int, name varchar)") await db.execute( "insert into foo values (?, ?), (?, ?)", (1, "Sally", 2, "Janet") ) await db.commit() threads = [Thread(target=runner, args=(k, db)) for k in range(4)] for thread in threads: thread.start() for thread in threads: thread.join() self.assertEqual(len(results), 4) for rows in results.values(): self.assertEqual(len(rows), 2) async def test_context_cursor(self): async with aiosqlite.connect(TEST_DB) as db: async with db.cursor() as cursor: await cursor.execute( "create table context_cursor " "(i integer primary key asc, k integer)" ) await cursor.executemany( "insert into context_cursor (k) values (?)", [[i] for i in range(10)], ) await db.commit() async with aiosqlite.connect(TEST_DB) as db: async with db.execute("select * from context_cursor") as cursor: rows = [] async for row in cursor: rows.append(row) assert len(rows) == 10 async def test_cursor_return_self(self): async with aiosqlite.connect(TEST_DB) as db: cursor = await db.cursor() result = await cursor.execute( "create table test_cursor_return_self (i integer, k integer)" ) self.assertEqual(result, cursor, "cursor execute returns itself") result = await cursor.executemany( "insert into test_cursor_return_self values (?, ?)", [(1, 1), (2, 2)] ) self.assertEqual(result, cursor) result = await cursor.executescript( "insert into test_cursor_return_self values (3, 3);" "insert into test_cursor_return_self values (4, 4);" "insert into test_cursor_return_self values (5, 5);" ) self.assertEqual(result, cursor) async def test_connection_properties(self): async with aiosqlite.connect(TEST_DB) as db: self.assertEqual(db.total_changes, 0) async with db.cursor() as cursor: self.assertFalse(db.in_transaction) await cursor.execute( "create table test_properties " "(i integer primary key asc, k integer, d text)" ) await cursor.execute( "insert into test_properties (k, d) values (1, 'hi')" ) self.assertTrue(db.in_transaction) await db.commit() self.assertFalse(db.in_transaction) self.assertEqual(db.total_changes, 1) self.assertIsNone(db.row_factory) self.assertEqual(db.text_factory, default_text_factory) async with db.cursor() as cursor: await cursor.execute("select * from test_properties") row = await cursor.fetchone() self.assertIsInstance(row, tuple) self.assertEqual(row, (1, 1, "hi")) with self.assertRaises(TypeError): _ = row["k"] db.row_factory = aiosqlite.Row db.text_factory = bytes self.assertEqual(db.row_factory, aiosqlite.Row) self.assertEqual(db.text_factory, bytes) async with db.cursor() as cursor: await cursor.execute("select * from test_properties") row = await cursor.fetchone() self.assertIsInstance(row, aiosqlite.Row) self.assertEqual(row[1], 1) self.assertEqual(row[2], b"hi") self.assertEqual(row["k"], 1) self.assertEqual(row["d"], b"hi") async def test_fetch_all(self): async with aiosqlite.connect(TEST_DB) as db: await db.execute( "create table test_fetch_all (i integer primary key asc, k integer)" ) await db.execute( "insert into test_fetch_all (k) values (10), (24), (16), (32)" ) await db.commit() async with aiosqlite.connect(TEST_DB) as db: cursor = await db.execute("select k from test_fetch_all where k < 30") rows = await cursor.fetchall() self.assertEqual(rows, [(10,), (24,), (16,)]) async def test_enable_load_extension(self): """Assert that after enabling extension loading, they can be loaded""" async with aiosqlite.connect(TEST_DB) as db: try: await db.enable_load_extension(True) await db.load_extension("test") except OperationalError as e: assert "not authorized" not in e.args except AttributeError: raise SkipTest( "python was not compiled with sqlite3 " "extension support, so we can't test it" ) async def test_set_progress_handler(self): """Assert that after setting a progress handler returning 1, DB operations are aborted""" async with aiosqlite.connect(TEST_DB) as db: await db.set_progress_handler(lambda: 1, 1) with self.assertRaises(OperationalError): await db.execute( "create table test_progress_handler (i integer primary key asc, k integer)" ) async def test_create_function(self): """Assert that after creating a custom function, it can be used""" def no_arg(): return "no arg" def one_arg(num): return num * 2 async with aiosqlite.connect(TEST_DB) as db: await db.create_function("no_arg", 0, no_arg) await db.create_function("one_arg", 1, one_arg) async with db.execute("SELECT no_arg();") as res: row = await res.fetchone() self.assertEqual(row[0], "no arg") async with db.execute("SELECT one_arg(10);") as res: row = await res.fetchone() self.assertEqual(row[0], 20) @skipUnless(sys.version_info < (3, 8), "Python < 3.8 specific behaviour") async def test_create_function_deterministic_pre38(self): """Make sure the deterministic parameter cannot be used in old Python versions""" def one_arg(num): return num * 2 async with aiosqlite.connect(TEST_DB) as db: with self.assertWarnsRegex(UserWarning, "registered as non-deterministic"): await db.create_function("one_arg", 1, one_arg, deterministic=True) await db.execute("create table foo (id int, bar int)") # Deterministic parameter is only available in Python 3.8+ so this # won't be deterministic with self.assertRaisesRegex( OperationalError, "non-deterministic functions prohibited in index expressions", ): await db.execute("create index t on foo(one_arg(bar))") @skipIf(sys.version_info < (3, 8), "Python 3.8+ specific behaviour") async def test_create_function_deterministic_post38(self): """Assert that after creating a deterministic custom function, it can be used. https://sqlite.org/deterministic.html """ def one_arg(num): return num * 2 async with aiosqlite.connect(TEST_DB) as db: await db.create_function("one_arg", 1, one_arg, deterministic=True) await db.execute("create table foo (id int, bar int)") # Non-deterministic functions cannot be used in indexes await db.execute("create index t on foo(one_arg(bar))") async def test_set_trace_callback(self): statements = [] def callback(statement: str): statements.append(statement) async with aiosqlite.connect(TEST_DB) as db: await db.set_trace_callback(callback) await db.execute("select 10") self.assertIn("select 10", statements) async def test_connect_error(self): bad_db = Path("/something/that/shouldnt/exist.db") with self.assertRaisesRegex(OperationalError, "unable to open database"): async with aiosqlite.connect(bad_db) as db: self.assertIsNone(db) # should never be reached with self.assertRaisesRegex(OperationalError, "unable to open database"): db = await aiosqlite.connect(bad_db) self.assertIsNone(db) # should never be reached async def test_iterdump(self): async with aiosqlite.connect(":memory:") as db: await db.execute("create table foo (i integer, k charvar(250))") await db.executemany( "insert into foo values (?, ?)", [(1, "hello"), (2, "world")] ) lines = [line async for line in db.iterdump()] self.assertEqual( lines, [ "BEGIN TRANSACTION;", "CREATE TABLE foo (i integer, k charvar(250));", "INSERT INTO \"foo\" VALUES(1,'hello');", "INSERT INTO \"foo\" VALUES(2,'world');", "COMMIT;", ], ) async def test_cursor_on_closed_connection(self): db = await aiosqlite.connect(TEST_DB) cursor = await db.execute("select 1, 2") await db.close() with self.assertRaisesRegex(ValueError, "Connection closed"): await cursor.fetchall() with self.assertRaisesRegex(ValueError, "Connection closed"): await cursor.fetchall() async def test_cursor_on_closed_connection_loop(self): db = await aiosqlite.connect(TEST_DB) cursor = await db.execute("select 1, 2") tasks = [] for i in range(100): if i == 50: tasks.append(asyncio.ensure_future(db.close())) tasks.append(asyncio.ensure_future(cursor.fetchall())) for task in tasks: try: await task except sqlite3.ProgrammingError: pass @skipIf(sys.version_info < (3, 7), "Test backup() on 3.7+") async def test_backup_aiosqlite(self): def progress(a, b, c): print(a, b, c) async with aiosqlite.connect(":memory:") as db1, aiosqlite.connect( ":memory:" ) as db2: await db1.execute("create table foo (i integer, k charvar(250))") await db1.executemany( "insert into foo values (?, ?)", [(1, "hello"), (2, "world")] ) await db1.commit() with self.assertRaisesRegex(OperationalError, "no such table: foo"): await db2.execute("select * from foo") await db1.backup(db2, progress=progress) async with db2.execute("select * from foo") as cursor: rows = await cursor.fetchall() self.assertEqual(rows, [(1, "hello"), (2, "world")]) @skipIf(sys.version_info < (3, 7), "Test backup() on 3.7+") async def test_backup_sqlite(self): async with aiosqlite.connect(":memory:") as db1: with sqlite3.connect(":memory:") as db2: await db1.execute("create table foo (i integer, k charvar(250))") await db1.executemany( "insert into foo values (?, ?)", [(1, "hello"), (2, "world")] ) await db1.commit() with self.assertRaisesRegex(OperationalError, "no such table: foo"): db2.execute("select * from foo") await db1.backup(db2) cursor = db2.execute("select * from foo") rows = cursor.fetchall() self.assertEqual(rows, [(1, "hello"), (2, "world")]) @skipUnless(sys.version_info < (3, 7), "Test short circuit fail on Py 3.6") async def test_backup_py36(self): async with aiosqlite.connect(":memory:") as db1, aiosqlite.connect( ":memory:" ) as db2: with self.assertRaisesRegex(RuntimeError, "backup().+3.7"): await db1.backup(db2) aiosqlite-0.17.0/docs/000077500000000000000000000000001401460023400145135ustar00rootroot00000000000000aiosqlite-0.17.0/docs/_static/000077500000000000000000000000001401460023400161415ustar00rootroot00000000000000aiosqlite-0.17.0/docs/_static/custom.css000066400000000000000000000004341401460023400201660ustar00rootroot00000000000000div.omnilib { margin-top: 24px; } div.omnilib-badges { margin-top: 12px; margin-bottom: 10px; } div.omnilib-badges a { color: #bbb; text-decoration: none; font-size: 24px; border: none; } div.omnilib-badges a:hover { color: #777; border: none; }aiosqlite-0.17.0/docs/_templates/000077500000000000000000000000001401460023400166505ustar00rootroot00000000000000aiosqlite-0.17.0/docs/_templates/badges.html000066400000000000000000000005261401460023400207660ustar00rootroot00000000000000 aiosqlite-0.17.0/docs/_templates/omnilib.html000066400000000000000000000016461401460023400211760ustar00rootroot00000000000000

The Omnilib Project

Omnilib is a group of MIT licensed software libraries developed under a common, inclusive Code of Conduct. We are committed to providing a welcoming and open space for all contributors who adhere to these rules.
   
aiosqlite-0.17.0/docs/api.rst000066400000000000000000000013551401460023400160220ustar00rootroot00000000000000 API Reference ============= .. module:: aiosqlite Connection ---------- .. autofunction:: connect .. autoclass:: Connection :special-members: __aenter__, __aexit__, __await__ Cursors ------- .. autoclass:: aiosqlite.cursor.Cursor :special-members: __aiter__, __anext__, __aenter__, __aexit__ Errors ------ .. autoexception:: Warning :members: .. autoexception:: Error :members: .. autoexception:: DatabaseError :members: .. autoexception:: IntegrityError :members: .. autoexception:: ProgrammingError :members: .. autoexception:: OperationalError :members: .. autoexception:: NotSupportedError :members: Advanced -------- .. autofunction:: register_adapter .. autofunction:: register_converter aiosqlite-0.17.0/docs/changelog.rst000066400000000000000000000001061401460023400171710ustar00rootroot00000000000000Changelog ========= .. mdinclude:: ../CHANGELOG.md :start-line: 2aiosqlite-0.17.0/docs/conf.py000066400000000000000000000053211401460023400160130ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- Project information ----------------------------------------------------- import datetime project = "aiosqlite" copyright = f"{datetime.date.today().year}, John Reese" author = "John Reese" # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "m2r", "sphinx.ext.autodoc", "sphinx.ext.intersphinx", ] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [] autodoc_default_options = { "show-inheritance": True, "members": True, "undoc-members": True, } autodoc_member_order = "groupwise" highlight_language = "python3" intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} master_doc = "index" # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = "alabaster" html_theme_options = { "description": "Sqlite for Python AsyncIO", "fixed_sidebar": True, "badge_branch": "master", "github_button": False, "github_user": "omnilib", "github_repo": "aiosqlite", "show_powered_by": False, "sidebar_collapse": False, "extra_nav_links": { "Report Issues": "https://github.com/omnilib/aiosqlite/issues", }, } html_sidebars = { "**": [ "about.html", "badges.html", "navigation.html", "relations.html", "searchbox.html", "omnilib.html", ], } # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] aiosqlite-0.17.0/docs/contributing.rst000066400000000000000000000001171401460023400177530ustar00rootroot00000000000000Contributing ============ .. mdinclude:: ../CONTRIBUTING.md :start-line: 2aiosqlite-0.17.0/docs/index.rst000066400000000000000000000005731401460023400163610ustar00rootroot00000000000000.. aiosqlite documentation master file, created by sphinx-quickstart on Sun Apr 26 19:57:46 2020. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. .. include:: ../README.rst .. toctree:: :hidden: :maxdepth: 2 api .. toctree:: :hidden: :maxdepth: 1 changelog contributing aiosqlite-0.17.0/makefile000066400000000000000000000014511401460023400152640ustar00rootroot00000000000000.venv: python -m venv .venv source .venv/bin/activate && make setup dev echo 'run `source .venv/bin/activate` to develop aiosqlite' venv: .venv setup: python -m pip install -Ur requirements-dev.txt dev: flit install --symlink release: lint test clean flit publish format: python -m usort format aiosqlite python -m black aiosqlite lint: python -m pylint --rcfile .pylint aiosqlite/*.py python -m usort check aiosqlite python -m black --check aiosqlite test: python -m coverage run -m aiosqlite.tests python -m coverage report python -m mypy aiosqlite/*.py perf: python -m unittest -v aiosqlite.tests.perf html: .venv README.rst docs/*.rst docs/conf.py .venv/bin/sphinx-build -b html docs html clean: rm -rf build dist html README MANIFEST *.egg-info distclean: clean rm -rf .venv aiosqlite-0.17.0/mypy.ini000066400000000000000000000001321401460023400152560ustar00rootroot00000000000000[mypy] ignore_missing_imports = True [mypy-aiosqlite.tests.perf] follow_imports = silent aiosqlite-0.17.0/pyproject.toml000066400000000000000000000020351401460023400164770ustar00rootroot00000000000000[build-system] requires = ["flit_core >=2,<4"] build-backend = "flit_core.buildapi" [tool.flit.metadata] module = "aiosqlite" author = "John Reese" author-email = "john@noswap.com" description-file = "README.rst" home-page = "https://aiosqlite.omnilib.dev" requires = ["typing_extensions>=3.7.2"] requires-python = ">=3.6" classifiers = [ "Development Status :: 5 - Production/Stable", "Framework :: AsyncIO", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Topic :: Software Development :: Libraries", ] [tool.flit.metadata.urls] Documentation = "https://aiosqlite.omnilib.dev/en/latest/" Github = "https://github.com/omnilib/aiosqlite" [tool.flit.sdist] exclude = [ ".github/", ] [tool.coverage.run] branch = true include = ["aiosqlite/*"] omit = ["aiosqlite/tests/*"] [tool.coverage.report] fail_under = 75 precision = 1 show_missing = true skip_covered = true [tool.isort] line_length = 88 multi_line_output = 3 force_grid_wrap = false include_trailing_comma = true use_parentheses = true aiosqlite-0.17.0/requirements-dev.txt000066400000000000000000000002751401460023400176270ustar00rootroot00000000000000aiounittest==1.4.0 attribution==1.4.0 black==20.8b1 codecov==2.1.11 coverage[toml]==5.4 flit==3.0.0 mypy==0.812 pylint==2.6.2 sphinx==3.5.1 usort==0.6.2 git+https://github.com/miyakogi/m2r