Add a function for reading JavaScript files
- ID
8be8cda- date
2024-08-16 21:16:16+00:00- author
Alex Chan <alex@alexwlchan.net>- parent
95da871- message
Add a function for reading JavaScript files- changed files
3 files, 87 additions, 3 deletions
Changed files
README.md (324) → README.md (486)
diff --git a/README.md b/README.md
index c51d079..1b3e83b 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,19 @@
# python-js-files
-This is a small collection of Python functions for manipulating JavaScript "data files" -- that is, JavaScript files that have a single variable that defines a JSON value.
-For example:
+This is a collection of Python functions for manipulating JavaScript "data files" -- that is, JavaScript files that have a single variable that defines a JSON value.
+
+This is an example of a JavaScript data file:
```javascript
const shape = { "sides": 5, "colour": "red" };
```
+Think of this module as the JSON module, but for JavaScript files.
+
## API
+* You can read a JavaScript file with `read_js(path, varname)`
+
## Why not use JSON files?
## License
src/javascript/__init__.py (22) → src/javascript/__init__.py (940)
diff --git a/src/javascript/__init__.py b/src/javascript/__init__.py
index 0b2f79d..ad71cd6 100644
--- a/src/javascript/__init__.py
+++ b/src/javascript/__init__.py
@@ -1 +1,36 @@
-__version__ = "1.1.3"
+import json
+import pathlib
+import typing
+
+
+__version__ = "1.0.0"
+
+
+def read_js(p: pathlib.Path | str, *, varname: str) -> typing.Any:
+ """
+ Read a JavaScript "data file".
+
+ For example, if you have a file `shape.js` with the following contents:
+
+ const redPentagon = { "sides": 5, "colour": "red" };
+
+ Then you can read it using this function:
+
+ >>> read_js('shape.js', varname='redPentagon')
+ {'sides': 5, 'colour': 'red'}
+
+ """
+ with open(p) as in_file:
+ contents = in_file.read()
+
+ if not contents.startswith(f"const {varname} = "):
+ raise ValueError(
+ f"File {p} does not start with JavaScript `const` declaration!"
+ )
+
+ if not contents.rstrip().endswith(";"):
+ raise ValueError(f"File {p} does not end with a trailing semicolon!")
+
+ json_string = contents.replace(f"const {varname} = ", "").rstrip().rstrip(";")
+
+ return json.loads(json_string)
tests/test_javascript.py (0) → tests/test_javascript.py (1569)
diff --git a/tests/test_javascript.py b/tests/test_javascript.py
new file mode 100644
index 0000000..afb03a8
--- /dev/null
+++ b/tests/test_javascript.py
@@ -0,0 +1,44 @@
+import pathlib
+
+import pytest
+
+from javascript import read_js
+
+
+class TestReadJs:
+ def test_can_read_file(self, tmp_path: pathlib.Path) -> None:
+ js_path = tmp_path / "shape.js"
+ js_path.write_text(
+ 'const redPentagon = {\n "sides": 5,\n "colour": "red"\n};\n'
+ )
+
+ assert read_js(js_path, varname="redPentagon") == {"sides": 5, "colour": "red"}
+
+ def test_non_existent_file_is_error(self) -> None:
+ with pytest.raises(FileNotFoundError):
+ read_js("doesnotexist.js", varname="shape")
+
+ def test_non_json_value_is_error(self, tmp_path: pathlib.Path) -> None:
+ js_path = tmp_path / "total.js"
+ js_path.write_text("const sum = 1 + 1 + 1;")
+
+ with pytest.raises(ValueError):
+ read_js(js_path, varname="sum")
+
+ def test_incorrect_varname_is_error(self, tmp_path: pathlib.Path) -> None:
+ js_path = tmp_path / "shape.js"
+ js_path.write_text(
+ 'const redPentagon = {\n "sides": 5,\n "colour": "red"\n};\n'
+ )
+
+ with pytest.raises(
+ ValueError, match="does not start with JavaScript `const` declaration"
+ ):
+ read_js(js_path, varname="blueTriangle")
+
+ def test_no_trailing_semicolon_is_error(self, tmp_path: pathlib.Path) -> None:
+ js_path = tmp_path / "shape.js"
+ js_path.write_text('const redPentagon = {\n "sides": 5,\n "colour": "red"\n}')
+
+ with pytest.raises(ValueError, match="does not end with a trailing semicolon"):
+ read_js(js_path, varname="redPentagon")