mirror of
https://github.com/infeeeee/dyn2py
synced 2025-12-16 22:16:18 +01:00
Compare commits
5 Commits
72cb52e0bf
...
0.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
| dda1d98fb2 | |||
| b2d851f5bc | |||
| c3737f0eb3 | |||
| 8909594a98 | |||
| e2aecc684f |
52
README.md
52
README.md
@@ -91,46 +91,66 @@ dyn2py --filter py --backup path/to/pythonfiles
|
|||||||
|
|
||||||
#### Git hooks
|
#### Git hooks
|
||||||
|
|
||||||
Git Hooks are a built-in feature of Git that allow developers to automate tasks throughout the Git workflow. Read more here: https://githooks.com/
|
Git hooks are a built-in feature of Git that allow developers to automate tasks throughout the Git workflow. Read more here: https://githooks.com/
|
||||||
|
|
||||||
With the `pre-commit` hook it's possible to add more files to the currently initialized commit.
|
With the `pre-commit` hook it's possible to add more files to the currently initialized commit.
|
||||||
|
|
||||||
You can find an example pre-commit hook here: [pre-commit](pre-commit). Copy this file to the `.git/hooks` folder of your repo of Dynamo graph. This folder is hidden by default, but it should exist in all initialized git repo. Do not rename this file.
|
You can find an example pre-commit hook here: [pre-commit](pre-commit). Copy this file to the `.git/hooks` folder of your repo of Dynamo graphs. This folder is hidden by default, but it should exist in all initialized git repo. Do not rename this file.
|
||||||
|
|
||||||
This script will go through staged `.dyn` files and export python scripts from them, and add them to the current commit. Now you can check check changed lines in a diff tool!
|
This script will go through staged `.dyn` files and export python scripts from them, and add them to the current commit. Now you can check changed lines in a diff tool, you can see changed python code in a PR!
|
||||||
|
|
||||||
### As a python module
|
### As a python module
|
||||||
|
|
||||||
Full API documentation available here: https://infeeeee.github.io/dyn2py
|
Full API documentation available here: https://infeeeee.github.io/dyn2py
|
||||||
|
|
||||||
Most basic example to extract all nodes next to a Dynamo file:
|
#### Examples
|
||||||
|
|
||||||
|
Extract all nodes from python nodes next to a Dynamo file:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import dyn2py
|
import dyn2py
|
||||||
|
|
||||||
|
# Open a Dynamo graph:
|
||||||
dynamo_file = dyn2py.DynamoFile("path/to/dynamofile.dyn")
|
dynamo_file = dyn2py.DynamoFile("path/to/dynamofile.dyn")
|
||||||
|
|
||||||
|
# Extract python nodes:
|
||||||
dynamo_file.extract_python()
|
dynamo_file.extract_python()
|
||||||
|
|
||||||
|
# Save all python nodes as separate files:
|
||||||
dyn2py.PythonFile.write_open_files()
|
dyn2py.PythonFile.write_open_files()
|
||||||
```
|
```
|
||||||
|
|
||||||
Change options like with the command line switches with the `Options` class:
|
Update python node from a python file:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import dyn2py
|
import dyn2py
|
||||||
|
|
||||||
# Create a backup on overwrite, read python files from a different folder:
|
# Open a python file:
|
||||||
options = dyn2py.Options(
|
python_file = dyn2py.PythonFile("path/to/pythonfile.py")
|
||||||
backup=True,
|
|
||||||
python_folder="path/to/pythonfiles")
|
|
||||||
|
|
||||||
|
# Update the node in the graph:
|
||||||
|
python_file.update_dynamo()
|
||||||
|
|
||||||
|
# Save modified Dynamo graph:
|
||||||
|
dyn2py.DynamoFile.write_open_files()
|
||||||
|
```
|
||||||
|
|
||||||
|
Update all python nodes of a Dynamo grapg from a different folder, save backups:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import dyn2py
|
||||||
|
|
||||||
|
# open a Dynamo graph:
|
||||||
dynamo_file = dyn2py.DynamoFile("path/to/dynamofile.dyn")
|
dynamo_file = dyn2py.DynamoFile("path/to/dynamofile.dyn")
|
||||||
python_files = dynamo_file.get_related_python_files(options)
|
|
||||||
|
# Get python files from a dofferent folder:
|
||||||
|
python_files = dynamo_file.get_related_python_files(python_folder="path/to/pythonfiles")
|
||||||
|
|
||||||
# Read python files and update the graph:
|
# Read python files and update the graph:
|
||||||
[python_file.update_dynamo(options) for python_file in python_files]
|
[python_file.update_dynamo() for python_file in python_files]
|
||||||
|
|
||||||
# Don't forget to save at the end:
|
# Save open Dynamo graphs:
|
||||||
dynamo_file.write(options)
|
dyn2py.DynamoFile.write_open_files(backup=True)
|
||||||
```
|
```
|
||||||
|
|
||||||
For more examples check tests in the [tests folder on Github](https://github.com/infeeeee/dyn2py/tree/main/tests)
|
For more examples check tests in the [tests folder on Github](https://github.com/infeeeee/dyn2py/tree/main/tests)
|
||||||
@@ -149,6 +169,8 @@ Only supports Dynamo 2 files, Dynamo 1 files are reported and ignored. Please up
|
|||||||
|
|
||||||
Both IronPython2 and CPython3 nodes are supported! IronPython2 nodes won't be updated to CPython3, they will be imported as-is.
|
Both IronPython2 and CPython3 nodes are supported! IronPython2 nodes won't be updated to CPython3, they will be imported as-is.
|
||||||
|
|
||||||
|
Cannot create new python nodes, only existing ones can be updated.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
@@ -166,7 +188,7 @@ With venv:
|
|||||||
```
|
```
|
||||||
git clone https://github.com/infeeeee/dyn2py
|
git clone https://github.com/infeeeee/dyn2py
|
||||||
cd dyn2py
|
cd dyn2py
|
||||||
venv .venv
|
python -m venv .venv
|
||||||
. ./.venv/bin/activate
|
. ./.venv/bin/activate
|
||||||
pip install -e .
|
pip install -e .
|
||||||
```
|
```
|
||||||
@@ -204,7 +226,7 @@ python -m unittest discover -v -s ./tests -p "test_*.py"
|
|||||||
### New release
|
### New release
|
||||||
|
|
||||||
1. Update version number in `pyproject.toml`
|
1. Update version number in `pyproject.toml`
|
||||||
2. Create a publish a git tag with that number
|
2. Create and publish a git tag with that number
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
1
TODO.md
1
TODO.md
@@ -7,6 +7,7 @@
|
|||||||
- [x] DynamoFile
|
- [x] DynamoFile
|
||||||
- [x] PythonFile
|
- [x] PythonFile
|
||||||
- [x] PythonNode
|
- [x] PythonNode
|
||||||
|
- [ ] Options
|
||||||
- [ ] run()
|
- [ ] run()
|
||||||
|
|
||||||
## CI/CD
|
## CI/CD
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
|
|||||||
Source: "dyn2py.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "dyn2py.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
|
||||||
[Icons]
|
[Icons]
|
||||||
Name: "{group}\dyn2py (cmd)"; Filename: "{cmd}"; WorkingDir: "{userdocs}"; Parameters: "/k dyn2py"
|
Name: "{group}\dyn2py (cmd)"; Filename: "{cmd}"; WorkingDir: "{userdocs}"; Parameters: "/k dyn2py -h"
|
||||||
Name: "{group}\dyn2py (powershell)"; Filename: "powershell"; WorkingDir: "{userdocs}"; Parameters: "-noexit -command dyn2py"
|
Name: "{group}\dyn2py (powershell)"; Filename: "powershell"; WorkingDir: "{userdocs}"; Parameters: "-noexit -command dyn2py -h"
|
||||||
|
|
||||||
[Code]
|
[Code]
|
||||||
const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
|
const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
|
||||||
|
|||||||
@@ -202,6 +202,6 @@ def run(options: Options) -> None:
|
|||||||
|
|
||||||
# Write files at the end:
|
# Write files at the end:
|
||||||
try:
|
try:
|
||||||
File.write_open_files()
|
File.write_open_files(options)
|
||||||
except File.Error as e:
|
except File.Error as e:
|
||||||
logging.error(f"Cannot save file! {e.file.filepath}")
|
logging.error(f"Cannot save file! {e.file.filepath}")
|
||||||
|
|||||||
@@ -112,21 +112,26 @@ class File():
|
|||||||
"""
|
"""
|
||||||
return bool(self.extension == ".py")
|
return bool(self.extension == ".py")
|
||||||
|
|
||||||
def write(self, options: Options | None = None) -> None:
|
def write(self, options: Options | None = None, **option_args) -> None:
|
||||||
"""Prepare writing file to the disk:
|
"""Prepare writing file to the disk:
|
||||||
create backup, process dry-run, call filetype specific write_file() methods
|
create backup, process dry-run, call filetype specific write_file() methods
|
||||||
Should be called on subclasses!
|
Should be called on subclasses!
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
options (Options | None, optional): Run options. Defaults to None.
|
options (Options | None, optional): Run options. Defaults to None.
|
||||||
|
**option_args: Options() arguments
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
|
ValueError: Both options and other arguments given
|
||||||
TypeError: If called on a File object
|
TypeError: If called on a File object
|
||||||
File.Error: Target folder does not exist
|
File.Error: Target folder does not exist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not options:
|
if not options:
|
||||||
options = Options()
|
options = Options.from_kwargs(kwargs=option_args)
|
||||||
|
elif option_args:
|
||||||
|
# Should not give both options and arguments:
|
||||||
|
raise ValueError("Options object and extra arguments!")
|
||||||
|
|
||||||
# This should only work on subclasses:
|
# This should only work on subclasses:
|
||||||
if type(self).__name__ == "File":
|
if type(self).__name__ == "File":
|
||||||
@@ -178,14 +183,22 @@ class File():
|
|||||||
isinstance(f, cls)}
|
isinstance(f, cls)}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def write_open_files(cls, options: Options | None = None) -> None:
|
def write_open_files(cls, options: Options | None = None, **option_args) -> None:
|
||||||
"""Write open files of this class and subclasses
|
"""Write open files of this class and subclasses
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
options (Options | None, optional): Run options. Defaults to None.
|
options (Options | None, optional): Run options. Defaults to None.
|
||||||
|
**option_args: Options() arguments
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Both options and other arguments given
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not options:
|
if not options:
|
||||||
options = Options()
|
options = Options.from_kwargs(kwargs=option_args)
|
||||||
|
elif option_args:
|
||||||
|
# Should not give both options and arguments:
|
||||||
|
raise ValueError("Options object and extra arguments!")
|
||||||
|
|
||||||
for f in cls.get_open_files():
|
for f in cls.get_open_files():
|
||||||
f.write(options)
|
f.write(options)
|
||||||
@@ -219,18 +232,25 @@ class DynamoFile(File):
|
|||||||
python_nodes: set[PythonNode]
|
python_nodes: set[PythonNode]
|
||||||
"""Python node objects, read from this file."""
|
"""Python node objects, read from this file."""
|
||||||
|
|
||||||
def extract_python(self, options: Options | None = None) -> list[PythonFile]:
|
def extract_python(self, options: Options | None = None, **option_args) -> list[PythonFile]:
|
||||||
"""Extract python files from Dynamo graphs, add them to open_files
|
"""Extract python files from Dynamo graphs, add them to open_files
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
options (Options | None, optional): Run options. Defaults to None.
|
options (Options | None, optional): Run options. Defaults to None.
|
||||||
|
**option_args: Options() arguments
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Both options and other arguments given
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[PythonFile]: List of PythonFile objects extracted from this DynamoFile
|
list[PythonFile]: List of PythonFile objects extracted from this DynamoFile
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not options:
|
if not options:
|
||||||
options = Options()
|
options = Options.from_kwargs(kwargs=option_args)
|
||||||
|
elif option_args:
|
||||||
|
# Should not give both options and arguments:
|
||||||
|
raise ValueError("Options object and extra arguments!")
|
||||||
|
|
||||||
logging.info(f"Extracting from file: {self.filepath}")
|
logging.info(f"Extracting from file: {self.filepath}")
|
||||||
python_files = []
|
python_files = []
|
||||||
@@ -372,17 +392,24 @@ class DynamoFile(File):
|
|||||||
with open(self.filepath, "w", encoding="utf-8", newline="") as output_file:
|
with open(self.filepath, "w", encoding="utf-8", newline="") as output_file:
|
||||||
json.dump(self.full_dict, output_file, indent=2, use_decimal=True)
|
json.dump(self.full_dict, output_file, indent=2, use_decimal=True)
|
||||||
|
|
||||||
def get_related_python_files(self, options: Options | None = None) -> list[PythonFile]:
|
def get_related_python_files(self, options: Options | None = None, **option_args) -> list[PythonFile]:
|
||||||
"""Get python files exported from this Dynamo file
|
"""Get python files exported from this Dynamo file
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
options (Options | None, optional): Run options. Defaults to None.
|
options (Options | None, optional): Run options. Defaults to None.
|
||||||
|
**option_args: Options() arguments
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Both options and other arguments given
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[PythonFile]: A list of PythonFile objects
|
list[PythonFile]: A list of PythonFile objects
|
||||||
"""
|
"""
|
||||||
if not options:
|
if not options:
|
||||||
options = Options()
|
options = Options.from_kwargs(kwargs=option_args)
|
||||||
|
elif option_args:
|
||||||
|
# Should not give both options and arguments:
|
||||||
|
raise ValueError("Options object and extra arguments!")
|
||||||
|
|
||||||
# Find the folder of the python files
|
# Find the folder of the python files
|
||||||
if options.python_folder:
|
if options.python_folder:
|
||||||
@@ -552,15 +579,23 @@ class PythonFile(File):
|
|||||||
logging.debug(f"Header data from python file: {self.header_data}")
|
logging.debug(f"Header data from python file: {self.header_data}")
|
||||||
# logging.debug(f"Code from python file: {self.code}")
|
# logging.debug(f"Code from python file: {self.code}")
|
||||||
|
|
||||||
def update_dynamo(self, options: Options | None = None) -> None:
|
def update_dynamo(self, options: Options | None = None, **option_args) -> None:
|
||||||
"""Update a the source Dynamo graph from this python script
|
"""Update a the source Dynamo graph from this python script
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
options (Options | None, optional): Run options. Defaults to None.
|
options (Options | None, optional): Run options. Defaults to None.
|
||||||
|
**option_args: Options() arguments
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Both options and other arguments given
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not options:
|
if not options:
|
||||||
options = Options()
|
options = Options.from_kwargs(kwargs=option_args)
|
||||||
|
elif option_args:
|
||||||
|
# Should not give both options and arguments:
|
||||||
|
raise ValueError("Options object and extra arguments!")
|
||||||
|
|
||||||
dynamo_file = self.get_source_dynamo_file()
|
dynamo_file = self.get_source_dynamo_file()
|
||||||
|
|
||||||
@@ -653,7 +688,7 @@ class PythonNode():
|
|||||||
python_file (PythonFile, optional): The python file to be converted to node. Defaults to None.
|
python_file (PythonFile, optional): The python file to be converted to node. Defaults to None.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
Error: Wrong arguments were given
|
PythonNode.Error: Wrong arguments were given
|
||||||
"""
|
"""
|
||||||
# Initialize from dynamo file:
|
# Initialize from dynamo file:
|
||||||
if node_dict_from_dyn and dynamo_file and not python_file:
|
if node_dict_from_dyn and dynamo_file and not python_file:
|
||||||
@@ -707,4 +742,5 @@ class PythonNode():
|
|||||||
self.checksum = hashlib.md5("".join(checksums).encode()).hexdigest()
|
self.checksum = hashlib.md5("".join(checksums).encode()).hexdigest()
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
|
"""Something wrong with this node"""
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -26,16 +26,13 @@ class Options(argparse.Namespace):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
source (list[pathlib.Path | str], optional): List of files to run on. Defaults to [].
|
source (list[pathlib.Path | str], optional): List of files to run on. Defaults to [].
|
||||||
loglevel (str, optional): log level. Defaults to DEFAULT_LOGLEVEL.
|
loglevel (str, optional): Log level. Defaults to DEFAULT_LOGLEVEL.
|
||||||
dry_run (bool, optional): If it's a dry run. Defaults to False.
|
dry_run (bool, optional): Dry run, do not save files. Defaults to False.
|
||||||
force (bool, optional): Overwrite files, even if they are older. Defaults to False.
|
force (bool, optional): Overwrite files, even if they are older. Defaults to False.
|
||||||
backup (bool, optional): Create backup of modified files. Defaults to False.
|
backup (bool, optional): Create backup of modified files. Defaults to False.
|
||||||
filter (str, optional): 'dyn' or 'py' file filter for running on folders. Defaults to "".
|
filter (str, optional): 'dyn' or 'py' file filter for running on folders. Defaults to "".
|
||||||
update (bool, optional): Update mode, like inverse on Dynamo files. Defaults to False.
|
update (bool, optional): Update mode, like inverse on Dynamo files. Defaults to False.
|
||||||
python_folder (pathlib.Path | str | None, optional): Path to export python files to, or import from there. Defaults to None.
|
python_folder (pathlib.Path | str | None, optional): Path to export python files to, or import from there. Defaults to None.
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If loglevel or filter is invalid
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.source = []
|
self.source = []
|
||||||
@@ -45,23 +42,58 @@ class Options(argparse.Namespace):
|
|||||||
else:
|
else:
|
||||||
self.source.append(s)
|
self.source.append(s)
|
||||||
|
|
||||||
if loglevel.upper() in LOGLEVELS:
|
self.loglevel = self.sanitize_option_string("loglevel", loglevel)
|
||||||
self.loglevel = loglevel.upper()
|
|
||||||
else:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
self.dry_run = dry_run
|
self.dry_run = dry_run
|
||||||
self.force = force
|
self.force = force
|
||||||
self.backup = backup
|
self.backup = backup
|
||||||
|
self.filter = self.sanitize_option_string("filter", filter)
|
||||||
if not filter or filter in FILTERS:
|
|
||||||
self.filter = filter
|
|
||||||
else:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
self.update = update
|
self.update = update
|
||||||
|
|
||||||
if isinstance(python_folder, str):
|
if isinstance(python_folder, str):
|
||||||
self.python_folder = pathlib.Path(python_folder)
|
self.python_folder = pathlib.Path(python_folder)
|
||||||
else:
|
else:
|
||||||
self.python_folder = python_folder
|
self.python_folder = python_folder
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sanitize_option_string(arg: str, value: str) -> str:
|
||||||
|
"""Sanitize string option values
|
||||||
|
|
||||||
|
Args:
|
||||||
|
arg (str): The name of the argument
|
||||||
|
value (str): The value
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: if the value is invalid
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The correct string
|
||||||
|
"""
|
||||||
|
|
||||||
|
if arg == "loglevel":
|
||||||
|
if value.upper() in LOGLEVELS:
|
||||||
|
sanitized_value = value.upper()
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid loglevel!")
|
||||||
|
elif arg == "filter":
|
||||||
|
if not value or value in FILTERS:
|
||||||
|
sanitized_value = value
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid filter!")
|
||||||
|
else:
|
||||||
|
sanitized_value = value
|
||||||
|
|
||||||
|
return sanitized_value
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_kwargs(cls, **kwargs) -> Options:
|
||||||
|
"""Initialize an Options object from kwargs
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Options: The initialized object
|
||||||
|
"""
|
||||||
|
o = cls()
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = cls.sanitize_option_string(key, value)
|
||||||
|
setattr(o, key, value)
|
||||||
|
return o
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "dyn2py"
|
name = "dyn2py"
|
||||||
version = "0.3.3"
|
version = "0.4.1"
|
||||||
description = "Extract python code from Dynamo graphs"
|
description = "Extract python code from Dynamo graphs"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
|
|||||||
@@ -3,15 +3,17 @@ import dyn2py
|
|||||||
|
|
||||||
INPUT_DIR = "tests/input_files"
|
INPUT_DIR = "tests/input_files"
|
||||||
OUTPUT_DIR = "tests/output_files"
|
OUTPUT_DIR = "tests/output_files"
|
||||||
|
TEMP_DIR = "tests/temp_files"
|
||||||
|
|
||||||
|
|
||||||
def cleanup_output_dir():
|
def cleanup_dirs():
|
||||||
output_dir = pathlib.Path(OUTPUT_DIR)
|
for p in [OUTPUT_DIR, TEMP_DIR]:
|
||||||
if output_dir.exists():
|
the_dir = pathlib.Path(p)
|
||||||
for f in output_dir.iterdir():
|
if the_dir.exists():
|
||||||
f.unlink()
|
for f in the_dir.iterdir():
|
||||||
else:
|
f.unlink()
|
||||||
output_dir.mkdir()
|
else:
|
||||||
|
the_dir.mkdir()
|
||||||
|
|
||||||
|
|
||||||
def extract_single_node_dyn(modify_py: bool = False):
|
def extract_single_node_dyn(modify_py: bool = False):
|
||||||
@@ -23,7 +25,7 @@ def extract_single_node_dyn(modify_py: bool = False):
|
|||||||
modify_py (bool, optional): Also do some changes on the exported file. Defaults to False.
|
modify_py (bool, optional): Also do some changes on the exported file. Defaults to False.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
cleanup_output_dir()
|
cleanup_dirs()
|
||||||
|
|
||||||
# Extract py:
|
# Extract py:
|
||||||
options = dyn2py.Options(python_folder=OUTPUT_DIR)
|
options = dyn2py.Options(python_folder=OUTPUT_DIR)
|
||||||
|
|||||||
@@ -36,8 +36,12 @@ class TestCommandLine(unittest.TestCase):
|
|||||||
self.assertFalse(p.stderr)
|
self.assertFalse(p.stderr)
|
||||||
|
|
||||||
dyn_sources = [
|
dyn_sources = [
|
||||||
{"filename": "python_nodes.dyn", "py_file_count": 6},
|
{"filename": "python_nodes.dyn", "output_file_count": 6},
|
||||||
{"filename": "single_node.dyn", "py_file_count": 1}
|
{"filename": "single_node.dyn", "output_file_count": 1}
|
||||||
|
]
|
||||||
|
|
||||||
|
py_sources = [
|
||||||
|
{"filename": "single_node_mod.py"},
|
||||||
]
|
]
|
||||||
|
|
||||||
dyn_sources_error = ["dynamo1file.dyn",
|
dyn_sources_error = ["dynamo1file.dyn",
|
||||||
@@ -46,6 +50,15 @@ class TestCommandLine(unittest.TestCase):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def run_command(args: list = []) -> dict:
|
def run_command(args: list = []) -> dict:
|
||||||
|
"""_summary_
|
||||||
|
|
||||||
|
Args:
|
||||||
|
args (list, optional): list of arguments to run. Defaults to [].
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict["stderr"]: error message
|
||||||
|
dict["python_file_mtimes"]: exported file in the output dir, and modification times
|
||||||
|
"""
|
||||||
argstring = " ".join(args)
|
argstring = " ".join(args)
|
||||||
process = subprocess.run(f"dyn2py -l WARNING {argstring}",
|
process = subprocess.run(f"dyn2py -l WARNING {argstring}",
|
||||||
capture_output=True, shell=True)
|
capture_output=True, shell=True)
|
||||||
@@ -76,7 +89,7 @@ class TestCommandLine(unittest.TestCase):
|
|||||||
|
|
||||||
test_dicts[0]["filenames"] = [
|
test_dicts[0]["filenames"] = [
|
||||||
test_dicts[0]["filename"]]
|
test_dicts[0]["filename"]]
|
||||||
if i == 0:
|
if i == 0 and len(source_dict) > 1:
|
||||||
# Create a multi file version on the first file:
|
# Create a multi file version on the first file:
|
||||||
d = {}
|
d = {}
|
||||||
for key in source_dict:
|
for key in source_dict:
|
||||||
@@ -91,7 +104,7 @@ class TestCommandLine(unittest.TestCase):
|
|||||||
for test_dict in test_dicts:
|
for test_dict in test_dicts:
|
||||||
|
|
||||||
if pfolder_option:
|
if pfolder_option:
|
||||||
file_dir = INPUT_DIR
|
file_dir = TEMP_DIR
|
||||||
pfolder_arg = f"{pfolder_option} {OUTPUT_DIR}"
|
pfolder_arg = f"{pfolder_option} {OUTPUT_DIR}"
|
||||||
else:
|
else:
|
||||||
file_dir = OUTPUT_DIR
|
file_dir = OUTPUT_DIR
|
||||||
@@ -113,7 +126,7 @@ class TestCommandLine(unittest.TestCase):
|
|||||||
def test_dyn_error(self):
|
def test_dyn_error(self):
|
||||||
for s in self.dyn_sources_error:
|
for s in self.dyn_sources_error:
|
||||||
|
|
||||||
cleanup_output_dir()
|
cleanup_dirs()
|
||||||
|
|
||||||
if pathlib.Path(f"{INPUT_DIR}/{s}").exists():
|
if pathlib.Path(f"{INPUT_DIR}/{s}").exists():
|
||||||
shutil.copy(f"{INPUT_DIR}/{s}",
|
shutil.copy(f"{INPUT_DIR}/{s}",
|
||||||
@@ -131,29 +144,40 @@ class TestCommandLine(unittest.TestCase):
|
|||||||
dyn_tests = self.generate_test_args(self.dyn_sources)
|
dyn_tests = self.generate_test_args(self.dyn_sources)
|
||||||
|
|
||||||
for s in dyn_tests:
|
for s in dyn_tests:
|
||||||
cleanup_output_dir()
|
cleanup_dirs()
|
||||||
|
|
||||||
|
# if no pythonfolder, everything should be in output ddir
|
||||||
if not s["pfolder_arg"]:
|
if not s["pfolder_arg"]:
|
||||||
for filename in s["filenames"]:
|
source_dir = OUTPUT_DIR
|
||||||
shutil.copy(f"{INPUT_DIR}/{filename}",
|
else:
|
||||||
f"{OUTPUT_DIR}/{filename}")
|
source_dir = TEMP_DIR
|
||||||
|
|
||||||
|
# copy source files:
|
||||||
|
for filename in s["filenames"]:
|
||||||
|
shutil.copy(f"{INPUT_DIR}/{filename}",
|
||||||
|
f"{source_dir}/{filename}")
|
||||||
|
|
||||||
|
|
||||||
|
# Open files normally
|
||||||
file_open = self.run_command(
|
file_open = self.run_command(
|
||||||
[s["pfolder_arg"], s['filepath']])
|
[s["pfolder_arg"], s['filepath']])
|
||||||
|
|
||||||
|
# Open without error:
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
bool(file_open["stderr"]), msg=file_open["stderr"])
|
bool(file_open["stderr"]), msg=file_open["stderr"])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(file_open["python_file_mtimes"]), s["py_file_count"])
|
len(file_open["python_file_mtimes"]), s["output_file_count"])
|
||||||
|
|
||||||
# Test no overwrite
|
# Test no overwrite
|
||||||
file_no_overwrite = self.run_command(
|
file_no_overwrite = self.run_command(
|
||||||
[s["pfolder_arg"], s['filepath']])
|
[s["pfolder_arg"], s['filepath']])
|
||||||
|
|
||||||
|
# Should give error, because they already exist:
|
||||||
self.assertTrue(bool(file_no_overwrite["stderr"]))
|
self.assertTrue(bool(file_no_overwrite["stderr"]))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(file_no_overwrite["python_file_mtimes"]), s["py_file_count"])
|
len(file_no_overwrite["python_file_mtimes"]), s["output_file_count"])
|
||||||
|
|
||||||
|
# The modify time shouldn't change as they were not overwritten:
|
||||||
for p in file_no_overwrite["python_file_mtimes"]:
|
for p in file_no_overwrite["python_file_mtimes"]:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
file_no_overwrite["python_file_mtimes"][p], file_open["python_file_mtimes"][p])
|
file_no_overwrite["python_file_mtimes"][p], file_open["python_file_mtimes"][p])
|
||||||
@@ -162,10 +186,12 @@ class TestCommandLine(unittest.TestCase):
|
|||||||
file_force = self.run_command(
|
file_force = self.run_command(
|
||||||
[s["pfolder_arg"], s["force_arg"], s['filepath']])
|
[s["pfolder_arg"], s["force_arg"], s['filepath']])
|
||||||
|
|
||||||
|
# Should not have an error:
|
||||||
self.assertFalse(bool(file_force["stderr"]))
|
self.assertFalse(bool(file_force["stderr"]))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(file_force["python_file_mtimes"]), s["py_file_count"])
|
len(file_force["python_file_mtimes"]), s["output_file_count"])
|
||||||
|
|
||||||
|
# Modify time should be higher as they were replaced
|
||||||
for p in file_force["python_file_mtimes"]:
|
for p in file_force["python_file_mtimes"]:
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
file_force["python_file_mtimes"][p] > file_open["python_file_mtimes"][p]
|
file_force["python_file_mtimes"][p] > file_open["python_file_mtimes"][p]
|
||||||
@@ -178,18 +204,52 @@ class TestCommandLine(unittest.TestCase):
|
|||||||
self.assertFalse(bool(file_backup["stderr"]))
|
self.assertFalse(bool(file_backup["stderr"]))
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(file_backup["python_file_mtimes"]), s["py_file_count"] * 2)
|
len(file_backup["python_file_mtimes"]
|
||||||
|
), s["output_file_count"] * 2,
|
||||||
|
msg=f""
|
||||||
|
)
|
||||||
|
|
||||||
for p in file_force["python_file_mtimes"]:
|
for p in file_force["python_file_mtimes"]:
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
file_backup["python_file_mtimes"][p] > file_force["python_file_mtimes"][p]
|
file_backup["python_file_mtimes"][p] > file_force["python_file_mtimes"][p]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# def test_py(self):
|
||||||
|
# py_tests = self.generate_test_args(self.py_sources)
|
||||||
|
|
||||||
|
# # TODO add more python files!
|
||||||
|
# self.assertEqual(len(py_tests), 1)
|
||||||
|
|
||||||
|
# for s in py_tests:
|
||||||
|
# cleanup_dirs()
|
||||||
|
|
||||||
|
# extract_single_node_dyn(modify_py=True)
|
||||||
|
|
||||||
|
|
||||||
|
# # if pythonfolder, python should be in the temp folder:
|
||||||
|
# if s["pfolder_arg"]:
|
||||||
|
# for filename in s["filenames"]:
|
||||||
|
# shutil.move(f"{OUTPUT_DIR}/{filename}",
|
||||||
|
# f"{TEMP_DIR}/{filename}")
|
||||||
|
|
||||||
|
# # Open files normally
|
||||||
|
# file_open = self.run_command(
|
||||||
|
# [s["pfolder_arg"], s['filepath']])
|
||||||
|
|
||||||
|
|
||||||
|
# # Open without error:
|
||||||
|
# self.assertFalse(
|
||||||
|
# bool(file_open["stderr"]), msg=file_open["stderr"])
|
||||||
|
# self.assertEqual(
|
||||||
|
# len(file_open["python_file_mtimes"]), s["output_file_count"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_single_dyn_dryrun(self):
|
def test_single_dyn_dryrun(self):
|
||||||
for s in self.dyn_sources:
|
for s in self.dyn_sources:
|
||||||
for arg in ["-n", "--dry-run"]:
|
for arg in ["-n", "--dry-run"]:
|
||||||
|
|
||||||
cleanup_output_dir()
|
cleanup_dirs()
|
||||||
|
|
||||||
shutil.copy(f"{INPUT_DIR}/{s['filename']}",
|
shutil.copy(f"{INPUT_DIR}/{s['filename']}",
|
||||||
f"{OUTPUT_DIR}/{s['filename']}")
|
f"{OUTPUT_DIR}/{s['filename']}")
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class TestDynamoFile(unittest.TestCase):
|
|||||||
dyn.get_python_node_by_id("wrongid")
|
dyn.get_python_node_by_id("wrongid")
|
||||||
|
|
||||||
def test_extract_python(self):
|
def test_extract_python(self):
|
||||||
cleanup_output_dir()
|
cleanup_dirs()
|
||||||
dyn2py.PythonFile.open_files.clear()
|
dyn2py.PythonFile.open_files.clear()
|
||||||
|
|
||||||
opt = dyn2py.Options(python_folder=OUTPUT_DIR)
|
opt = dyn2py.Options(python_folder=OUTPUT_DIR)
|
||||||
@@ -69,7 +69,7 @@ class TestDynamoFile(unittest.TestCase):
|
|||||||
dyn2py.DynamoFile.get_open_file_by_uuid("76de5c79-17c5-4c74-9f90-ad99a213d339"))
|
dyn2py.DynamoFile.get_open_file_by_uuid("76de5c79-17c5-4c74-9f90-ad99a213d339"))
|
||||||
|
|
||||||
def test_get_related_python_files(self):
|
def test_get_related_python_files(self):
|
||||||
cleanup_output_dir()
|
cleanup_dirs()
|
||||||
|
|
||||||
opt = dyn2py.Options(python_folder=OUTPUT_DIR)
|
opt = dyn2py.Options(python_folder=OUTPUT_DIR)
|
||||||
dyn1 = dyn2py.DynamoFile(f"{INPUT_DIR}/python_nodes.dyn")
|
dyn1 = dyn2py.DynamoFile(f"{INPUT_DIR}/python_nodes.dyn")
|
||||||
@@ -91,7 +91,7 @@ class TestDynamoFile(unittest.TestCase):
|
|||||||
self.assertFalse(no_python_files)
|
self.assertFalse(no_python_files)
|
||||||
|
|
||||||
def test_write_same(self):
|
def test_write_same(self):
|
||||||
cleanup_output_dir()
|
cleanup_dirs()
|
||||||
|
|
||||||
shutil.copy(f"{INPUT_DIR}/python_nodes.dyn",
|
shutil.copy(f"{INPUT_DIR}/python_nodes.dyn",
|
||||||
f"{OUTPUT_DIR}/python_nodes.dyn")
|
f"{OUTPUT_DIR}/python_nodes.dyn")
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ from tests.support import *
|
|||||||
|
|
||||||
class TestFile(unittest.TestCase):
|
class TestFile(unittest.TestCase):
|
||||||
|
|
||||||
# Write methods should be tested in subclasses!
|
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
paths = [
|
paths = [
|
||||||
f"{INPUT_DIR}/python_nodes.dyn",
|
f"{INPUT_DIR}/python_nodes.dyn",
|
||||||
@@ -71,7 +69,7 @@ class TestFile(unittest.TestCase):
|
|||||||
nonexisting_file = dyn2py.File(f"{INPUT_DIR}/new_file.py")
|
nonexisting_file = dyn2py.File(f"{INPUT_DIR}/new_file.py")
|
||||||
|
|
||||||
# Extract a python file so it is always newer than the others:
|
# Extract a python file so it is always newer than the others:
|
||||||
cleanup_output_dir()
|
cleanup_dirs()
|
||||||
opt = dyn2py.Options(python_folder=OUTPUT_DIR)
|
opt = dyn2py.Options(python_folder=OUTPUT_DIR)
|
||||||
older_file.extract_python(options=opt) # type: ignore
|
older_file.extract_python(options=opt) # type: ignore
|
||||||
for f in dyn2py.PythonFile.open_files:
|
for f in dyn2py.PythonFile.open_files:
|
||||||
@@ -103,3 +101,15 @@ class TestFile(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(file.is_dynamo_file(), f == "dyn")
|
self.assertEqual(file.is_dynamo_file(), f == "dyn")
|
||||||
self.assertEqual(file.is_python_file(), f == "py")
|
self.assertEqual(file.is_python_file(), f == "py")
|
||||||
|
|
||||||
|
def test_write(self):
|
||||||
|
|
||||||
|
# new empty file:
|
||||||
|
empty_filepath = pathlib.Path(f"{OUTPUT_DIR}/empty.txt")
|
||||||
|
empty_filepath.touch()
|
||||||
|
|
||||||
|
empty_file = dyn2py.File(f"{OUTPUT_DIR}/empty.txt")
|
||||||
|
|
||||||
|
self.assertTrue(empty_file.exists)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
empty_file.write()
|
||||||
Reference in New Issue
Block a user