{ "info": { "author": "Nicholas Gaya", "author_email": "nickgaya@users.noreply.github.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Software Development :: Libraries" ], "description": "python2\n=======\n\nA library for running Python 2 code from a Python 3 application.\n\n Effortlessly harness the power and convenience of Python 2... in Python 3!\n\nWhy?\n----\n\nWhy not??\n\nThis library was created for more whimsical than practical reasons. In theory,\nit could be used to interface with legacy Python 2 code which for one reason or\nanother cannot be ported to Python 3.\n\nInstallation\n------------\n``python2`` requires a working install of both Python 2 and Python 3.\nCurrently the library has only been tested with Python 2.7 and Python 3.4, 3.5,\nand 3.6.\n\nTo install the package::\n\n pip install -U python2\n\nIf using virtualenvs, you will need to create separate Python 2 and 3\nvirtualenvs, and install the package into both.\n\nUsage\n-----\nTo begin working with Python 2, import the package in Python 3 and create a new\n``Python2`` object::\n\n >>> from python2.client import Python2\n >>> py2 = Python2('/path/to/python2/executable')\n\nThis object is our gateway to the Python 2 world. Python 2 builtins can be\naccessed as attributes of the ``Python2`` object. Let's use Python 2's\n``__import__()`` function to import the deprecated ``sha`` module, which was\nremoved in Python 3::\n\n >>> py2_sha = py2.__import__('sha')\n >>> py2_sha.sha('abc')\n >\n\nAhh, just like the good ol' days. You can deprecate a module but you can't\ndeprecate the human spirit!\n\nWe can use the ``Python2.project()`` method to convert Python 3 objects to\nPython 2::\n\n >>> py2.project(1)\n \n >>> py2.project('foo')\n \n\nYou can use ``Python2.lift()`` to lift Python 2 objects back to Python 3. For\ncontainer types, use ``Python2.deeplift()`` to recursively perform the lifting.\n``Py2Object`` instances have special properties ``_`` and ``__`` to perform the\nequivalent operations::\n\n >>> o = py2.project([1, 2, 3])\n >>> o\n \n >>> o._\n [, , ]\n >>> o.__\n [1, 2, 3]\n\nPython 2 objects can be used pretty much like regular Python 3 objects. You\ncan also freely mix and match with Python 3 builtin types::\n\n >>> x = py2.project(1)\n >>> x\n \n >>> str(x)\n '1'\n >>> x + 1\n \n >>> d = py2.dict(foo=x, bar=None)\n >>> d['foo'] is x\n True\n >>> del d['foo']\n >>> d\n \n >>> d.__\n {'bar': None}\n\nIf you just want to execute some Python 2 code directly, you can use the\n``Python2.exec()`` method. This method accepts a string containing Python 2\ncode and an optional dict representing the scope to execute the code in, and\nreturns the resulting scope after executing the code. This can be used to\ndefine new Python 2 classes and functions::\n\n >>> scope = py2.exec(\"\"\"\n ... def foo(x):\n ... return x + 1\n ... \"\"\")\n >>> foo = scope['foo']\n >>> foo(2)\n \n\nIf an exception occurs in Python 2, a ``Py2Error`` will be thrown by the\nclient. The Python 2 exception is stored as the ``exception`` attribute of the\n``Py2Error`` object. The underlying traceback is attached to the Python 2\nexception as the ``__traceback__`` attribute.\n\n::\n\n >>> py2.int('asdf')\n Traceback (most recent call last):\n File \"\", line 1, in \n ...\n python2.client.exceptions.Py2Error: ValueError: invalid literal for int() with base 10: 'asdf'\n\nWhen you're done using Python 2, you can end the session by calling the\n``Python2.shutdown()`` method. You can also use the ``Python2`` object as a\ncontext manager to automatically do the same thing when exiting the context.\n\n::\n\n >>> py2.shutdown()\n\nTesting\n-------\nThis package uses Tox for testing. Tests are not included in the Python dist,\nso you will need to clone the repo to run them. To run the unit tests, install\nTox and run the following command from the project's base directory::\n\n tox\n\nAfter running tox, you can run the client-server integration tests with the\n'integration_tests.sh' script. This script takes two arguments specifying the\nTox virtualenvs to use for Python 2 and 3, respectively::\n\n ./integration_tests.sh py27 py36\n\nTo modify the behavior of Tox, you can set the ``PYTEST_ADDOPTS`` variable.\nFor example, you can set the ``-x`` flag to abort after the first test\nfailure::\n\n export PYTEST_ADDOPTS=-x\n\nYou can use the ``-n NUM`` flag to parallelize the tests using the\n`pytest-xdist plugin`_ This adds some overhead to the test setup, so this\noption is primarily useful for speeding up the integration tests.\n\n export PYTEST_ADDOPTS='-n 4'\n\n.. _pytest-xdist plugin: http://pytest.org/dev/xdist.html\n\nCaveats\n-------\n\nSupported types\n```````````````\nProjection is only supported for basic builtin types. Other objects cannot be\nprojected to Python 2. The supported types are: ``bool``, ``int``, ``float``,\n``complex``, ``bytes``, ``unicode``, ``bytearray``, ``range``, ``slice``,\n``list``, ``tuple``, ``set``, ``frozenset``, and ``dict``. The ``None``,\n``NotImplemented``, and ``Ellipsis`` singletons are also supported.\n\nIn particular, Python 3 functions, types, and instances of user-defined classes\ncannot currently be projected into Python 2.\n\nType introspection\n``````````````````\nThe ``Py2Object`` class implements many \"magic methods\" from the Python 3 data\nmodel. As a result, a ``Py2Object`` appears to be callable, iterable, etc.,\neven if the underlying object is not. Attempting to perform such operations may\nresult in a ``Py2Error``.\n\nIf you need to introspect a Python 2 object, use the corresponding *Python 2*\nbuiltin functions. For example::\n\n >>> i = py2.project(1)\n >>> py2.callable(i)\n \n >>> py2.isinstance(i, py2.int)\n \n\nString types\n````````````\nIn Python 2, ``str`` objects are raw byte strings, while in Python 3 they are\nUnicode strings. This can lead to some confusion, as projecting a Python 3\n``str`` will result in a Python 2 ``unicode`` object, while lifting a Python 2\n``str`` will return a Python 3 ``bytes`` object.\n\n >>> py2.project('foo')\n \n >>> py2.lift(py2.str(123))\n b'123'\n\nDivision\n````````\nThe behavior of the division operator changed with `PEP 238`_. This created\ntwo alternate division operations, \"true division\" and \"classic division\".\nClassic division was removed in Python 3.\n\nTo respect this change, when two ``Py2Object`` s are divided, classic division\nis used. When a ``Py2Object`` divides or is divided by a Python 3 value, true division is used.\n\n::\n\n >>> i = py2.project(1)\n >>> j = py2.project(2)\n >>> i / j # classic division\n \n >>> i / 2 # true division\n \n >>> 1 / j # true division\n \n\n.. _PEP 238: https://www.python.org/dev/peps/pep-0238/\n\nFurther discussion\n------------------\n\nHow it works\n````````````\nWhen you launch a Python 2 session, the library spawns a child process running\nPython 2. This child process runs a *server* that listens for commands from\nthe Python 3 *client*. For each command, the server performs an operation in\nPython 2 and returns the result either as an encoded *value* made up of\nsupported types, or a *reference* to a Python 2 object stored on the server.\n\nOn the client side, the library wraps Python 2 references with the\n``Py2Object`` class. This class implements many of the \"magic methods\" of the\n`Python 3 data model`_ by sending commands to the Python 2 server to perform\nthe appropriate operation on the underlying Python 2 object.\n\n.. _Python 3 data model: https://docs.python.org/3/reference/datamodel.html\n\nCall-by-value semantics\n```````````````````````\nWhen projecting a value or calling a Python 2 function with Python 3 arguments,\nthe arguments will be passed to Python 2 \"by value\", that is, by encoding the\nvalue of the argument to be decoded by the server. When using a Python 2\nobject, the object is stored in the Python 2 session and is passed \"by\nreference\".\n\nThis has some implications for the semantics of Python 2 functions. Suppose we\nhave a Python 2 function that mutates a list. If we pass this function a\nPython 3 list, the list will be copied into Python 2 and the copy will be\nmutated, but the original will not be modified::\n\n >>> f = py2.eval(\"lambda l: l.append(1)\")\n >>> l = []\n >>> f(l)\n \n >>> l\n []\n\nHowever, if we project the list into Python 2 before passing it to the\nfunction, then we can observe the modifications on the projected list::\n\n >>> py2_l = py2.project(l)\n >>> f(py2_l)\n \n >>> py2_l\n \n\nReturn semantics\n````````````````\nReturning generally occurs by reference except for operations that require a\nspecific return type (``str()``, ``int()``, etc.). The main reason for this is\nthat returning by value may lose information about object identity that needs\nto be preserved. Return values can be easily lifted to Python 2 if desired.\n\nObject identity and lifespan\n````````````````````````````\nEach Python 2 object returned by the server is represented by a unique\n``Py2Object``. This means that the ``is`` operator can be used to determine if\ntwo ``Py2Object`` s refer to the same underlying object.\n\nThe Python 2 server stores all objects it returns, to prevent them from being\ndeallocated. When the corresponding ``Py2Object`` is deallocated in the Python\n3 process, the underlying Python 2 object will be removed from the server cache\nto allow it to be deallocated as appropriate.\n\nEncoding algorithm\n``````````````````\nThis library uses a simple JSON encoding for supported types. For a given\nfunction call, each unique object will only be encoded once. This means that\ndata structures with circular references are supported. For a detailed\ndescription of the algorithm, see the ``python2.shared.codec`` module.\n\nPossible improvements\n---------------------\n\nPython 2 types\n``````````````\nCurrently there is a single type for Python 2 objects in Python 3,\n``Py2Object``. An alternate strategy would be to dynamically create Python 3\nclasses for each Python 2 type encountered, and create proxy objects as\ninstances of these classes.\n\nThe main benefit of this change would be better type introspection for Python 2\nobjects (see the discussion at `Type introspection`_). However, it would be\nmore cumbersome and incur a performance cost, since the client would need to\nknow the type of each object and the methods supported by that type.\nAdditionally, this approach would not fully support the dynamic nature of the\nPython type system, since the proxied type would not reflect changes to the\nunderlying type such as adding or removing methods.\n\nThis would require the server to return the object type for references, and\nsome mechanism for the client to introspect Python 2 types. The client would\ncache types for the lifetime of the Python 2 session, with a mechanism to\nexplicitly refresh a type to pick up any changes that had occurred in Python 2.\n\nBootstrapping the type system might be a little tricky. We would want to\ncreate a type ``Py2type`` such all proxy types are instances of, *including\n``Py2type`` itself.* We would also probably want a base type for all proxy\nobjects, including types.\n\nPython 3 proxy objects in Python 2\n``````````````````````````````````\nCurrently the relationship between client and server is asymmetrical. The\nclient has a representation of Python 2 objects, but the server does not have\na way to represent Python 3 objects. We might like to add such a mechanism.\nThis would mean that instead of the simple request-response pattern from client\nto server we have now, there would be the possibility of callbacks. In effect,\nthe two processes would act more like coroutines with the flow of control\npassing back and forth between them.\n\nBetter Python version support\n`````````````````````````````\nWe could extend support to more Python 2 and 3 versions.\n\nSimilar projects\n----------------\nAfter writing this library, I discovered that I'm not the only one to have had\nthis idea. `Sux`_ is a library that provides similar functionality, with some\nnotable differences:\n\n- The library is much smaller and more lightweight, and only needs to be\n installed in the Python 3 environment to work.\n\n- The main emphasis is on imports and function calls, which makes sense since\n these are the most important operations for the using legacy packages. Most\n other operators (e.g. arithmetic operators) are not supported.\n\n- The library uses Pickle to communicate between the Python 2 and 3 processes.\n This is a good idea and I should probably have done the same, although I had\n fun implementing the current encoding algorithm.\n\n.. _Sux: https://github.com/nicois/sux/\n", "description_content_type": null, "docs_url": null, "download_url": "https://github.com/nickgaya/python2/tarball/1.2", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/nickgaya/python2", "keywords": "python2,legacy,compatibility", "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "python2", "package_url": "https://pypi.org/project/python2/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/python2/", "project_urls": { "Download": "https://github.com/nickgaya/python2/tarball/1.2", "Homepage": "https://github.com/nickgaya/python2" }, "release_url": "https://pypi.org/project/python2/1.2/", "requires_dist": null, "requires_python": null, "summary": "A library for running Python 2 code from a Python 3 application.", "version": "1.2" }, "last_serial": 2654677, "releases": { "1.1": [ { "comment_text": "", "digests": { "md5": "fff8760790c713e30d95577069166678", "sha256": "e1edc9b99ca53f02cb53e890919d8a20fc6deb0d5bbd5de32e4de5084982ad91" }, "downloads": -1, "filename": "python2-1.1.tar.gz", "has_sig": false, "md5_digest": "fff8760790c713e30d95577069166678", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15442, "upload_time": "2017-02-18T08:22:07", "url": "https://files.pythonhosted.org/packages/7a/dc/05cff0a37a272f2bbc00ec00cf351528bf53559e493a8671645ad8ed7d27/python2-1.1.tar.gz" } ], "1.2": [ { "comment_text": "", "digests": { "md5": "c45fe5ae2353b471e21ba15e66398aa2", "sha256": "9219352345a5cfcdf2e104c469bcbfe5b959b8e15f194988809b984a3fcdda55" }, "downloads": -1, "filename": "python2-1.2.tar.gz", "has_sig": false, "md5_digest": "c45fe5ae2353b471e21ba15e66398aa2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15915, "upload_time": "2017-02-20T12:00:59", "url": "https://files.pythonhosted.org/packages/7d/b5/fa7609a6ae251044c5751b14caae6bc29c129956ad251afd2d9cfcf7be6e/python2-1.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c45fe5ae2353b471e21ba15e66398aa2", "sha256": "9219352345a5cfcdf2e104c469bcbfe5b959b8e15f194988809b984a3fcdda55" }, "downloads": -1, "filename": "python2-1.2.tar.gz", "has_sig": false, "md5_digest": "c45fe5ae2353b471e21ba15e66398aa2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15915, "upload_time": "2017-02-20T12:00:59", "url": "https://files.pythonhosted.org/packages/7d/b5/fa7609a6ae251044c5751b14caae6bc29c129956ad251afd2d9cfcf7be6e/python2-1.2.tar.gz" } ] }