{ "info": { "author": "Rob King", "author_email": "jking@deadpixi.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries" ], "description": "Introduction\n============\nThis module provides a simple wrapper mechanism that abstracts away\ndifferences in various `DB-API`_ modules. It is compatible with both Python\n2.7 and Python 3.x.\n\nThe Python DB-API specifies a standardized set of mechanisms to access\ndifferent database engines. However, the specification allows for\nconsiderable leeway in how SQL statements and queries are presented to the\nuser, including different parameter quoting styles.\n\nThis module abstracts away those differences and also allows for SQL\nstatements and queries to be completely isolated from Python code. It serves\nas a very lightweight database abstraction library, and makes it possible\nto switch database engines by swapping out a single configuration file.\nUnlike many full-fledged ORM systems, it does not impose any structural\nrequirements on the database itself and in fact encourages programmers to\nwrite arbitrarily complex queries that take advantage of the database's\nnative ability to manipulate data.\n\nThe core of the module is the Database class, which represents a connection\nto a database. Database objects are instantiated with a configuration that\ndescribes how to connect to a database (what module to use and what arguments\nto pass to the module's `connect` function), and what queries will be run\non the database. Configurations are just Python dictionaries (or any other\nobject that inherits from the `collections.Mapping` abstract base class),\nand can be easily read from JSON files or `ConfigParser` objects. Here is\na simple example configuration that connects to a SQLite in-memory database:\n\n >>> config = {\n ... \"MODULE\": {\n ... \"name\": \"sqlite3\"\n ... },\n ... \"DATABASE\": {\n ... \"database\": \":memory:\"\n ... },\n ... \"QUERIES\": {\n ... \"create_table\": \"CREATE TABLE users (name TEXT NOT NULL PRIMARY KEY, password TEXT NOT NULL)\",\n ... \"create_user\": \"INSERT INTO users(name, password) VALUES(${name}, ${password})\",\n ... \"list_users\": \"SELECT * FROM users ORDER BY name ASC\",\n ... \"get_password\": \"SELECT password FROM users WHERE name = ${name}\",\n ... \"delete_user\": \"DELETE FROM users WHERE name = ${name}\"\n ... }\n ... }\n\nWe can create a database using this configuration and create a test\ntable using the \"create_table\" query that we defined (in a real world\nimplementation, the database would probably have already been populated,\nbut this serves well for an example):\n\n >>> db = Database(config)\n >>> result = db.create_table()\n\nNote how the queries we've defined become methods on the Database object\nwe created.\n\nWe can call methods on the database object to execute queries, passing\nparameters in as necessary. The parameters are automatically converted to\nthe module's appropriate paramter quoting mechanism:\n\n >>> result = db.create_user(name=\"bruce\", password=\"iamthenight\")\n >>> result = db.create_user(name=\"arthur\", password=\"glublub\")\n\nThe arguments passed to the query are safely substituted into the queries\ndefined in the configuration. Queries return results as lists of rows\npassed through a row factory function; by default this turns each row into\na dictionary mapping column names to values:\n\n >>> db.list_users() == [{\"name\": \"arthur\", \"password\": \"glublub\"}, {\"name\": \"bruce\", \"password\": \"iamthenight\"}]\n True\n\nA different factory function can be specified at instantiation time.\nIt takes two arguments: a DB-API standard cursor object and a row, and can\nreturn any value. For example, here is the default row factory function:\n\n >>> def row_factory(cursor, row):\n ... return {name[0]: value for name, value in zip(cursor.description, row)}\n\n >>> db2 = Database(config, row_factory)\n >>> result = db2.create_table()\n >>> result = db2.create_user(name=\"dick\", password=\"batmanrules\")\n >>> db2.list_users() == [{\"name\": \"dick\", \"password\": \"batmanrules\"}]\n True\n\nConnection and Transaction Contexts\n===================================\nThe Database class also acts as a context manager:\n\n >>> with Database(config) as db:\n ... result = db.create_table()\n ... result = db.create_user(name=\"hal\", password=\"brightestday\")\n ... result = db.list_users()\n\nThe Transaction class also provides a context manager that implements\ntransactions:\n\n >>> db = Database(config)\n >>> result = db.create_table()\n >>> try:\n ... with Transaction(db):\n ... result = db.create_user(name=\"hal\", password=\"brightestday\")\n ... result = db.create_user(name=\"hal\", password=\"darkestnight\")\n ... except Exception:\n ... print(\"transaction failed\")\n transaction failed\n\nAnd note that because the failure happened within a transaction, nothing\nwas added to the database:\n\n >>> db.list_users()\n []\n\nThis mechanism has the nice benefit that transactions can include non-database\nrelated statements within the context that will cause an automatic transaction\nrollback should they fail.\n\nUnsafe Substitutions\n====================\nThe \"QUERIES\" section of the database configuration allows parameterization\nusing `string.Template` syntax. These substitutions are automatically\nconverted to the module's native substitution format (`qmark`, `named`, etc).\nThese substitutions can appear in arbitrarily complex queries:\n\n >>> config[\"QUERIES\"][\"update_password\"] = \"UPDATE users SET password = COALESCE(${password}, password) WHERE name = ${name}\"\n >>> with Database(config) as db:\n ... result = db.create_table()\n ... result = db.create_user(name=\"clark\", password=\"greatcaesarsghost\")\n ... result = db.update_password(name=\"clark\", password=\"visitbeautifulkandor\")\n ... db.list_users() == [{\"name\": \"clark\", \"password\": \"visitbeautifulkandor\"}]\n True\n\nHowever, many database engines only allow certain portions of queries to be\nparameterized using parameter substitution. Often, \"structural\" components\nin a query (the names of tables, columns used for sorting, sort order,\nlimits) cannot be substituted using the module's substitution mechanism.\nFor these sorts of situations, unsafe substitution can be used. Note that\nthe name means what it says: using this form of substitution can result in\nSQL injection attacks, so use them wisely!\n\nUnsafe substitutions are indicated by using normal Python string interpolation\nsyntax. For example:\n\n >>> config[\"QUERIES\"][\"list_users\"] = \"SELECT * FROM users ORDER BY name %(order)s\"\n >>> db = Database(config)\n >>> result = db.create_table()\n >>> result = db.create_user(name=\"ralghul\", password=\"lazarus\")\n >>> result = db.create_user(name=\"ocobblepot\", password=\"wahwahwah\")\n >>> db.list_users(order=\"DESC\") == [{\"name\": \"ralghul\", \"password\": \"lazarus\"}, {\"name\": \"ocobblepot\", \"password\": \"wahwahwah\"}]\n True\n >>> db.list_users(order=\"ASC\") == [{\"name\": \"ocobblepot\", \"password\": \"wahwahwah\"}, {\"name\": \"ralghul\", \"password\": \"lazarus\"}]\n True\n\nTesting This Module\n===================\nThis module has embedded doctests that are run with the module is invoked\nfrom the command line. Simply run the module directly to run the tests.\n\nContact Information and Licensing\n=================================\nThis module was written by Rob King (jking@deadpixi.com).\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Lesser General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU Lesser General Public License for more details.\n\nYou should have received a copy of the GNU Lesser General Public License\nalong with this program. If not, see .", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/deadpixi/dpdb", "keywords": null, "license": "https://www.gnu.org/licenses/lgpl.txt", "maintainer": null, "maintainer_email": null, "name": "dpdb", "package_url": "https://pypi.org/project/dpdb/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/dpdb/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/deadpixi/dpdb" }, "release_url": "https://pypi.org/project/dpdb/0.1.0/", "requires_dist": null, "requires_python": null, "summary": "A simple DB-API abstraction module.", "version": "0.1.0" }, "last_serial": 1691954, "releases": { "0.1.0": [] }, "urls": [] }