Import PyScript Version into Python Runtime (#958)

* Import current version as PyScript.__version__ and PyScript.version_info

* Add integration, python unit tests
 
* Mock pyscript.py with __mocks__/pyscript.js to make jest tests happy
This commit is contained in:
Jeff Glass
2022-11-18 11:49:45 -06:00
committed by GitHub
parent 0e1c396d7c
commit 687b93d148
9 changed files with 118 additions and 7 deletions

View File

@@ -0,0 +1,8 @@
# `__version__`
`PyScript.__version__` is a `str` representing the current version of PyScript in a human-readable form. For a structured version more suitable to comparisons, and for details of what each part of the version number represents, see [`version_info`](version_info.md)
```shell
>>> pyscript.__version__
'2023.02.1.dev'
```

View File

@@ -0,0 +1,16 @@
# `version_info`
`PyScript.version_info` is a `namedtuple` representing the current version of PyScript. It can be used to compare whether current version precedes or follows a desired version. For a human-readable version of the same info, see [`__version__`](__version__.md)
```sh
>>> pyscript.version_info
version_info(year=2023, month=2, minor=1, releaselevel='dev')
```
## Version Fields
| **parameter** | **CalVer equivalent field** | **example value** | **description** |
|---------------|-------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------|
| `year` | Full year (YYYY) | 2023 | The year of the release; when printed or represented as a string, always written with 4 digits |
| `month` | Short Month (MM) | 2 | The month of the release; when printed or represented as a string, written with 1 or 2 digits as necessary |
| `minor` | | 1 | The incremental number of the release for this month; when printed or represented as a string, written with 1 or two digits as necessary |
| `releaselevel` | | 'dev' | A string representing the qualifications of this build. |

View File

@@ -0,0 +1,14 @@
/**
* this file mocks the `src/python/pyscript.py` file
* since importing of `.py` files isn't usually supported
* inside JS/TS files.
*
* It sets the value of whatever is imported from
* `src/python/pyscript.py` the contents of that file
*
* This is needed since the imported object is further
* passed to a function which only accepts a string.
*/
const fs = require('fs');
module.exports = fs.readFileSync('./src/python/pyscript.py', 'utf8');

View File

@@ -1,12 +1,16 @@
/**
* this file mocks the `src/python/pyscript.py` file
* since importing of `.py` files isn't usually supported
* inside JS/TS files.
*
* It sets the value of whatever is imported from
* `src/python/pyscript.py` to be an empty string i.e. ""
* this file mocks python files that are not explicitly
* matched by a regex in jest.config.js, since importing of
* `.py` files isn't usually supported inside JS/TS files.
*
* This is needed since the imported object is further
* passed to a function which only accepts a string.
*
* The mocked contents of the `.py` file will be "", e.g.
* nothing.
*/
module.exports = "";
console.warn(`.py files that are not explicitly mocked in \
jest.config.js and /__mocks__/ are mocked as empty strings`);
module.exports = "";

View File

@@ -17,6 +17,7 @@ module.exports = {
url: 'http://localhost',
},
moduleNameMapper: {
'^.*?pyscript\.py$': '<rootDir>/__mocks__/_pyscript.js',
'^[./a-zA-Z0-9$_-]+\\.py$': '<rootDir>/__mocks__/fileMock.js',
'\\.(css)$': '<rootDir>/__mocks__/cssMock.js',
},

View File

@@ -5,6 +5,7 @@ import type { loadPyodide as loadPyodideDeclaration, PyodideInterface, PyProxy }
// eslint-disable-next-line
// @ts-ignore
import pyscript from './python/pyscript.py';
import { version } from './runtime';
import type { AppConfig } from './pyconfig';
import type { Stdio } from './stdio';
@@ -71,6 +72,7 @@ export class PyodideRuntime extends Runtime {
logger.info('importing pyscript.py');
this.run(pyscript as string);
this.run(`PyScript.set_version_info('${version}')`)
logger.info('pyodide loaded and initialized');
}

View File

@@ -4,6 +4,7 @@ import base64
import html
import io
import time
from collections import namedtuple
from textwrap import dedent
import micropip # noqa: F401
@@ -136,6 +137,39 @@ class PyScript:
)
)
@classmethod
def set_version_info(cls, version_from_runtime: str):
"""Sets the __version__ and version_info properties from provided JSON data
Args:
version_from_runtime (str): A "dotted" representation of the version:
YYYY.MM.m(m).releaselevel
Year, Month, and Minor should be integers; releaselevel can be any string
"""
# __version__ is the same string from runtime.ts
cls.__version__ = version_from_runtime
# version_info is namedtuple: (year, month, minor, releaselevel)
version_parts = version_from_runtime.split(".")
version_dict = {
"year": int(version_parts[0]),
"month": int(version_parts[1]),
"minor": int(version_parts[2]),
}
# If the version only has three parts (e.g. 2022.09.1), let the releaselevel be ""
try:
version_dict["releaselevel"] = version_parts[3]
except IndexError:
version_dict["releaselevel"] = ""
# Format mimics sys.version_info
_VersionInfo = namedtuple("version_info", version_dict.keys())
cls.version_info = _VersionInfo(**version_dict)
# tidy up class namespace
del cls.set_version_info
def set_current_display_target(target_id):
get_current_display_target._id = target_id

View File

@@ -1,3 +1,5 @@
import re
import pytest
from .support import JsErrors, PyScriptTest
@@ -147,3 +149,26 @@ class TestBasic(PyScriptTest):
pyscript_tag = self.page.locator("py-script")
assert pyscript_tag.inner_html() == ""
def test_python_version(self):
self.pyscript_run(
"""
<py-script>
import js
js.console.log(PyScript.__version__)
js.console.log(str(PyScript.version_info))
</py-script>
"""
)
assert (
re.match(r"\d{4}\.\d{2}\.\d+\.[a-zA-Z0-9]+", self.console.log.lines[-2])
is not None
)
assert (
re.match(
r"version_info\(year=\d{4}, month=\d{2}, "
r"minor=\d+, releaselevel='[a-zA-Z0-9]+'\)",
self.console.log.lines[-1],
)
is not None
)

View File

@@ -117,3 +117,10 @@ def test_uses_top_level_await():
"""
)
assert pyscript.uses_top_level_await(src) is False
def test_set_version_info():
version_string = "1234.56.78.ABCD"
pyscript.PyScript.set_version_info(version_string)
assert pyscript.PyScript.__version__ == version_string
assert pyscript.PyScript.version_info == (1234, 56, 78, "ABCD")