{"id":19378164,"url":"https://github.com/almarklein/translate_to_legacy","last_synced_at":"2025-10-11T12:08:06.010Z","repository":{"id":72713809,"uuid":"49941038","full_name":"almarklein/translate_to_legacy","owner":"almarklein","description":"Single module to translate Python 3 code to Python 2.7","archived":false,"fork":false,"pushed_at":"2016-02-19T10:25:45.000Z","size":17,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-07T05:28:31.354Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/almarklein.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-01-19T09:20:22.000Z","updated_at":"2018-11-30T19:13:03.000Z","dependencies_parsed_at":"2023-02-23T03:00:19.246Z","dependency_job_id":null,"html_url":"https://github.com/almarklein/translate_to_legacy","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/almarklein%2Ftranslate_to_legacy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/almarklein%2Ftranslate_to_legacy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/almarklein%2Ftranslate_to_legacy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/almarklein%2Ftranslate_to_legacy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/almarklein","download_url":"https://codeload.github.com/almarklein/translate_to_legacy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240511295,"owners_count":19813237,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T09:05:03.171Z","updated_at":"2025-10-11T12:08:00.991Z","avatar_url":"https://github.com/almarklein.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# translate_to_legacy\n\nSingle module to translate Python 3 code to Python 2.7. Write all your\ncode in Python 3, and convert it to Python 2.7 at install time.\n\n\n### Purpose\n\nPython 3 was first released in 2008. Initially people mostly wrote code\nin Python 2, and converted that to Python 3 using 2to3. Later, it became\nmore popular to support both Python versions from a single code base\nusing e.g. six.py. Although this works well, it restricts the developer\nfrom writing pretty Python 3 code. The aim of this project is to allow\ndevelopers to write in Python 3, and support Python 2 using a\ntranslation step at build time.\n\nThis project is an alternative to lib3to2, but only uses a tokenizer\n(and not an ast parser), which makes it faster and small enough to fit\nin one module. This is enough to handle most fixes. Translations for\nimports are handled differently though, see below. Strings/unicode/bytes\nare also handled differtently (better IMHO) by this module.\n\n\n### Limitations\n\nFor this to work, not all Python 3 functionality can be used. E.g. type\nannotations, the `@` operator, and `nonlocal`. Further, not all relevant\nfixers might be implemented yet, because for now I've focussed on what\nI need. Fixers are easily added though (see below).\n\n\n### General usage\n\nIn your `setup.py` add the following code (or similar):\n\n```python\nfrom translate_to_legacy import LegacyPythonTranslator\nshutil.copytree(original_dir, legacy_dir)\nLegacyPythonTranslator.translate_dir(legacy_dir, skip=files_to_skip)\n``` \n\nFor a bit more fine-grained control, here is how the translator class\ncan be used to translate strings from individual files:\n\n```python\nfrom translate_to_legacy import LegacyPythonTranslator\ntranslator = LegacyPythonTranslator(code)\nnew_code = translator.translate()\n```\n\nTo adopt this approach in your project and still allow single-source\ndistribution:\n  \n* add one module to the root of your project.\n* in `setup.py`\n  [invoke the translation](https://github.com/zoofIO/flexx/blob/master/setup.py#L56)\n  at build time.\n* in the root `__init__.py` add \n  [two lines](https://github.com/zoofIO/flexx/blob/master/flexx/__init__.py#L32-L33)\n  to make legacy Python use the translated code.\n* probably make a few modification to make the translations work correctly.\n* optionally add more translations by subclassing `LegacyPythonTranslator`.\n* resolve tricky situations like `isinstance(x, bytes)`.\n\n\n### The translator\n\nThe translator is the main class that manages the parsing and\ntranslation process. The fixes that it applies are implemented as\nmethods that are prefixed with `fix_`. \n\nThe `BaseTranslator` provides the basic functionality, and the\n`LegacyPythonTranslator` implements the fixers specific for the purpose\nto translate to legacy Python. To add more fixers, simply subclass and\nadd some methods. Existing fixers can be disabled by setting the\ncorresponding class attribute to None.\n\nThe `BaseTranslator` class has the following attributes:\n    \n* `translate()` - apply the fixers to the tokens and return the result\n  as a string. This should usually be all you need.\n* `tokens` - the list of found tokens.\n* `dump()` - get the result as a string (translate() calls this).\n* `translate_dir()` - classmethod to translate all .py files in the given\n  directory and its subdirectories. Skips files that match names\n  in skip (which can be full file names, absolute paths, and paths\n  relative to dirname). Any file that imports 'print_function'\n  from __future__ is cancelled.\n\n\n### How to write a custom fixer\n\nTo implement a custom fixer, create a subclass of the translator class and\nimplement a method prefixed with `fix_`:\n    \n```\nclass MyTranslator(LegacyPythonTranslator):\n    \n    def fix_range(self, token):\n        if token.type == 'identifier' and token.text == 'range':\n            if token.next_char == '(' and token.prev_char != '.':\n                token.fix = 'xrange'\n    \n    def fix_make_legacy_slow(self, token):\n        if token.type == 'keyword' and token.text == 'return':\n            indent = token.indentation * ' '\n            t = Token(token.total_text, 'custom', token.start, token.start)\n            t.fix = '\\n%simport time; time.sleep(0.1)\\n' % indent\n            return t\n```\n\nThe code snippet above contains an example to make use of `xrange`,\nwhich is a standard fixer. One can see how the fix is applied by setting\nthe `fix` attribute. In the second (less serious) fixer, a new token\nis returned to insert a piece of code.\n\n\n### The tokens\n\nA token is a unit piece of code. This module only generates tokens for\nconstructs of interest, e.g. operators are not present in tokens. Each\ntoken specifies its positionin the total text, so that replacements can\nbe easily made, without scrambling the text too much.\n\nThe fixers receive one token at a time, and must use it to determine\nif a fix should be applied. To do this, surrounding tokens and\ncharacters can be inspected. To apply a fix, simply set the `fix`\nattribute.\n\nThe `Token` class has the following attributes:\n    \n* `type` - the type of token: 'comment', 'string', 'keyword',\n  'number' or 'identifier'.\n* `total_text` - the total text that the token is part of.\n* `text` - the original text of the token.\n* `start` - the start position in the total text.\n* `end` - the end position in the total text.\n* `fix` - the string to replace this token with.\n* `prev_token` - the token to the left of this token.\n* `next_token` - the token to the right of this token.\n* `prev_char` - the first non-whitespace char to the left of this token\n  that is still on the same line.\n* `next_char` - the first non-whitespace char to the right of this token\n  that is still on the same line.\n* `line_tokens` - all (non-comment) tokens that are on the same line.\n* `find_forward()` - find the position of a character to the right.\n* `find_forward()` - find the position of a character to the left.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falmarklein%2Ftranslate_to_legacy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falmarklein%2Ftranslate_to_legacy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falmarklein%2Ftranslate_to_legacy/lists"}