Package: ts2python
ts2pythonPython-interoperability for Typescript-Interfaces.
Transpiles TypeScript-Interface-definitions to Python
TypedDicts, plus support for run-time type-checking
of JSON-data.License and Source Codets2python is open source software under theApache 2.0 LicenseCopyright 2021-2023 Eckhart Arnoldarnold@badw.de, Bavarian Academy of Sciences and HumanitiesLicensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License athttps://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.The complete source-code of ts2python can be downloaded from thegit-repository.PurposeWhen processing JSON data, as for example, form aJSON-RPCcall, with Python, it would
be helpful to have Python-definitions of the JSON-structures at
hand, in order to solicit IDE-Support, static type checking and,
potentially to enable structural validation at runtime.There exist different technologies for defining the structure of
JSON-data. Next toJSON-schema, a
de facto very popular technology for defining JSON-obejcts areTypescript-Interfaces.
For example, thelanguage server protocoldefines the structure of the JSON-data exchanged between client
and server with Typescript-Interfaces.In order to enable structural validation on the Python-side,
ts2python transpiles the typescript-interface definitions
to Python-data structure definitions, primarily,TypedDicts,
but with some postprocessing it can also be adjusted to
other popular models for records or data structures in
Python, e.g.pydantic-Classes
and the like.ts2python aims to support translation of TypeScript-Interfaces on two
different tiers:Tier 1: Transpilation of passive data-structures, that is,
Typescript-definition-files that contain only data definitions
and no function definitions and, in particular,
only "passive" Typescript-Interface that define data-structures
but do not contain any methods.Tier 2: Tanspilation of active data-structures, function-
and method-definitions, i.e. Translation of (almost) any
Typescript-definition-file.StatusPresently, Tier 1 support, i.e. transpilation of passive data
structures works quite well. So, for example, all Interfaces
from thelanguage server protocol V3.16can be transpiled to Python Typed-Dicts.Tier 2 support is still very much work in progress. I am
using "vscode.d.ts"-definition file as test-case. Some things work,
but there are still some unsupported constructs, and the Python
code emitted for features that go beyond Tier 1 may not even
be valid Python all the time! Please, keep that in mind.The documentation presently only covers Tier 1 support.Installationts2python can be installed from the command line with the command:$ pip install ts2pythonts2python requires the parsing-expression-grammar-frameworkDHParserwhich will automatically be installed as a dependency by
thepip-command. ts2python requires at least Python Version 3.8
to run. (If there is any interest, I might backport it to Python 3.6.)
However, the Python-code it produces is backwards compatible
down to Python 3.6, if thetyping extensionshave been installed.UsageIn order to generate TypedDict-classes from Typescript-Interfaces,
runts2pythonon the Typescript-Interface definitions:$ ts2python interfaces.tsThis generates a .py-file in same directory as the source
file that contains the TypedDict-classes and can simpy be
imported in Python-Code:from interfaces import *Json-data which adheres to a specific structure (no matter
whether defined on the typescript side via interfaces or
on the Python-side via TypedDicts) can easily be interchanged
and deserialized:import json
request_msg: RequestMessage = json.loads(input_data)The root-type (RootMessagein the above example) can
be arbitrarily complex and deeply nested.Calling ts2python from another Python-script:The ts2python-Parser can, of course, also be accessed directly
from Python with the followin imports and function calls:from ts2python.ts2pthonParser import process_file
...
process_file("SOURCE.ts", "DESTINATION.py")Or, use:from ts2pthon.ts2pythonParser import compile_src, serialize_result
...
result, errors = compile_src(DOCUMENT)
if errors:
    for e in errors:  print(e)
else:
   print(serialize_result(result))Validationts2python ships support for runtime-type validation. While type
errors can be detected by static type checkers, runtime type
validation can be useful when processing data from an outside
source which cannot statically be checked, like, for example,
json-data stemming from an RPC-call. ts2python runtime-type
validation can be invoked via dedicated functions or via
decorator as in this example:from ts2python.json_validation import TypedDict, type_check

class Position(TypedDict, total=True):
    line: int
    character: int 

class Range(TypedDict, total=True):
    start: Position
    end: Position

@type_check
def middle_line(rng: Range) -> Position:
    line = (rng['start']['line'] + rng['end']['line']) // 2
    character = 0
    return Position(line=line, character=character)

data = {'start': {'line': 1, 'character': 1},
       'end': {'line': 8, 'character': 17}}
assert middle_line(data) == {'line': 4, 'character': 0}

malformed_data = {'start': 1, 'end': 8}
middle_line(malformed_data)  # <- TypeError raised by @type_checkWith the type decorator the last call fails with a TypeError:TypeError: Parameter "rng" of function "middle_line" failed the type-check, because:
Type error(s) in dictionary of type <class '__main__.Range'>:
Field start: '1' is not of <class '__main__.Position'>, but of type <class 'int'>
Field end: '8' is not of <class '__main__.Position'>, but of type <class 'int'>Both the call and the return types can be validated.Type CheckersThe output ts2python is somewhat more liberal than what strict
typecheckers like mypy or pylance seem to allow. In particular
class definitions inside TypedDicts are considered illegal by
the specification und thus marked as an error by some
type-checkers. Use the command-line switch "-a toplevel" to
generate Python-code that is more acceptable to type checkers.Full DocumentationSeets2python.readthedocs.iofor the comprehensive
documentation of ts2pythonTests and DemonstrationThegit-repository of ts2pythoncontains unit-tests
as well asdoctests.
After cloning ts2python from the git-repository with:$ git clone https://github.com/jecki/ts2pythonthe unit tests can be found in thetestssubdirectory.
Both the unit and the doctests can be run by changing to thetests-sub-directory and calling therunner.py-skript therein.$ cd tests
$ python runner.pyIt is also possible to run the tests withpytestornose, in case you have
either of theses testing-frameworks installed.For a demonstration how the TypeScript-Interfaces are transpiled
to Python-code, run thedemo-3-16.sh-script (ordemo-3-16.baton Windows)
in the "demo"-sub-directory or the ts2python-repository.(Presently, this does not work with the LSP 3.17 specs, because the
specs contain typescript bugs at a few places and thus would need to
be corrected by hand after downloading.)Or, run thetst_ts2python_grammar.pyin the ts2python-directory
and look up the grammar-test-reports in the "REPORT"-sub-directory
of the "test_grammar"-subdirectory.