Repository: pyscript/pyscript

Issue:
PyScript should include version information
### Checklist

- [X] I added a descriptive title
- [X] I searched for other feature requests and couldn't find a duplicate (including also the ``type-feature`` tag)
- [X] I confirmed that it's not related to another project are area (see the above section)

### What is the idea?

Each release of PyScript should include a version and make it printable on the console or have it available somewhere 

### Why is this needed

It's important for users to  know what version they are in so they can know what's supported on that version and what not. It can also help with bug reporting, triaging, etc...

### What should happen?

A user should be able to get that information on the console (we can decide if we want to have it by default or ask users to add a flag in <py-config> , like debug: true.

Also, should be able to type `__version__` or `pyscript.__version__` in the REPL and be able to see the current version (or something similar)

### Additional Context

_No response_


Discussion:
Is anyone working on this? I'd be happy to give it a shot.

For access and format, I'd suggest the following, similar to both sys.version_info and [what IPython does](https://github.com/ipython/ipython/blob/c97f6d2ebb9a2eee5bc6531ecaf96dcb0649dadd/IPython/core/release.py#L18-L33): :

`pyscript.version_info()` returns a namedtuple(year, month, day, releaselevel, serial)
`pyscript.__version__` returns a string concatenated with '.', possibly just YYYY.MM.DD?

For example, for a tagged release of version **2023.02.1**:

```python
>>> pyscript.version_info()
version_info(year=2023, month=2, day=1, releaselevel='final', serial=0)
>>> pyscript.__version__
'2023.02.1`
```
 
I've been looking at ways to have these values automatically be set either on pre-release or release, so the tags don't have to be manually updated. The `releaselevel` parameter I imagine would be 'unstable' or 'alpha' in general, so the latest unstable builds can be identified at runtime if needed, and would be 'final' only for tagged releases.
@JeffersGlass no one is working on this.
If you have the space, please go for it! ☺️
Working on it!  The version-reporting part is done and fairly straightforward, the more complicated part is figuring out how it integrates with the build/CI/CD system so that the versioning is updated automatically in a consistent way.

I'll open a WIP PR with progress.

Resolving patch:
diff --git a/pyscriptjs/src/python/pyscript.py b/pyscriptjs/src/python/pyscript.py
--- a/pyscriptjs/src/python/pyscript.py
+++ b/pyscriptjs/src/python/pyscript.py
@@ -4,6 +4,7 @@
 import html
 import io
 import time
+from collections import namedtuple
 from textwrap import dedent
 
 import micropip  # noqa: F401
@@ -136,6 +137,39 @@ def write(element_id, value, append=False, exec_id=0):
             )
         )
 
+    @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


Test patch:
diff --git a/pyscriptjs/tests/integration/test_01_basic.py b/pyscriptjs/tests/integration/test_01_basic.py
--- a/pyscriptjs/tests/integration/test_01_basic.py
+++ b/pyscriptjs/tests/integration/test_01_basic.py
@@ -1,3 +1,5 @@
+import re
+
 import pytest
 
 from .support import JsErrors, PyScriptTest
@@ -147,3 +149,26 @@ def test_py_script_src_not_found(self):
 
         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
+        )
diff --git a/pyscriptjs/tests/py-unit/test_pyscript.py b/pyscriptjs/tests/py-unit/test_pyscript.py
--- a/pyscriptjs/tests/py-unit/test_pyscript.py
+++ b/pyscriptjs/tests/py-unit/test_pyscript.py
@@ -117,3 +117,10 @@ async def foo():
         """
     )
     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")