Skip to content

tasks: Environment class to encapsulate all compile-time information #841

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/periodic-integration-test-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
run: |
invoke release-local

# BROKEN:
# - name: Build and test using PyInstaller
# run: |
# invoke release-pyinstaller
Expand Down
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ development = [
# psutil is needed to reap Uvicorn's zombie processes when running end2end
# tests. One day someone finds a better solution.
"psutil",

# Nuitka:WARNING: Using very slow fallback for ordered sets, please install
# 'ordered-set' PyPI package for best Python compile time performance.
"nuitka",
"ordered-set",
]

[project.scripts]
Expand Down
8 changes: 3 additions & 5 deletions strictdoc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import os
from strictdoc.core.environment import SDocRuntimeEnvironment

__version__ = "0.0.32"

STRICTDOC_ROOT_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..")
)
assert os.path.isabs(STRICTDOC_ROOT_PATH), f"{STRICTDOC_ROOT_PATH}"

environment = SDocRuntimeEnvironment(__file__)
52 changes: 28 additions & 24 deletions strictdoc/cli/cli_arg_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from enum import Enum
from typing import List, Optional

from strictdoc.core.environment import SDocRuntimeEnvironment
from strictdoc.core.project_config import ProjectConfig

EXPORT_FORMATS = ["html", "html-standalone", "rst", "excel", "reqif-sdoc"]
Expand Down Expand Up @@ -281,8 +282,16 @@ def __init__(self, input_file, output_file):


class ServerCommandConfig:
def __init__(self, *, input_path: str, output_path: str, reload: bool):
def __init__(
self,
*,
environment: SDocRuntimeEnvironment,
input_path: str,
output_path: str,
reload: bool,
):
assert os.path.exists(input_path)
self.environment: SDocRuntimeEnvironment = environment
abs_input_path = os.path.abspath(input_path)
self.input_path: str = abs_input_path
self.output_path: str = output_path
Expand All @@ -298,7 +307,7 @@ class ExportMode(Enum):
class ExportCommandConfig: # pylint: disable=too-many-instance-attributes
def __init__( # pylint: disable=too-many-arguments
self,
strictdoc_root_path,
environment: SDocRuntimeEnvironment,
input_paths,
output_dir: str,
project_title: Optional[str],
Expand All @@ -309,7 +318,7 @@ def __init__( # pylint: disable=too-many-arguments
experimental_enable_file_traceability,
):
assert isinstance(input_paths, list), f"{input_paths}"
self.strictdoc_root_path = strictdoc_root_path
self.environment: SDocRuntimeEnvironment = environment
self.input_paths: List[str] = input_paths
self.output_dir: str = output_dir
self.project_title: Optional[str] = project_title
Expand All @@ -333,27 +342,15 @@ def get_export_mode(self):
return ExportMode.STANDALONE
raise NotImplementedError

@property
def strictdoc_root_path(self):
return self.environment.path_to_strictdoc

def get_static_files_path(self):
if getattr(sys, "frozen", False):
# If the application is run as a bundle, the PyInstaller bootloader
# extends the sys module by a flag frozen=True and sets the app
# path into variable _MEIPASS'.
bundle_dir = sys._MEIPASS # pylint: disable=protected-access
return os.path.join(bundle_dir, "_static")
return os.path.join(
self.strictdoc_root_path, "strictdoc/export/html/_static"
)
return self.environment.get_static_files_path()

def get_extra_static_files_path(self):
if getattr(sys, "frozen", False):
# If the application is run as a bundle, the PyInstaller bootloader
# extends the sys module by a flag frozen=True and sets the app
# path into variable _MEIPASS'.
bundle_dir = sys._MEIPASS # pylint: disable=protected-access
return os.path.join(bundle_dir, "_static_extra")
return os.path.join(
self.strictdoc_root_path, "strictdoc/export/html/_static_extra"
)
return self.environment.get_extra_static_files_path()

def integrate_project_config(self, project_config: ProjectConfig):
if self.project_title is None:
Expand Down Expand Up @@ -410,7 +407,10 @@ def get_passthrough_config(self) -> PassthroughCommandConfig:
self.args.input_file, self.args.output_file
)

def get_export_config(self, strictdoc_root_path) -> ExportCommandConfig:
def get_export_config(
self, environment: SDocRuntimeEnvironment
) -> ExportCommandConfig:
assert isinstance(environment, SDocRuntimeEnvironment)
project_title: Optional[str] = self.args.project_title

output_dir = self.args.output_dir if self.args.output_dir else "output"
Expand All @@ -419,7 +419,7 @@ def get_export_config(self, strictdoc_root_path) -> ExportCommandConfig:
output_dir = os.path.join(cwd, output_dir)

return ExportCommandConfig(
strictdoc_root_path,
environment,
self.args.input_paths,
output_dir,
project_title,
Expand All @@ -440,8 +440,12 @@ def get_import_config_excel(self, _) -> ImportExcelCommandConfig:
self.args.input_path, self.args.output_path, self.args.parser
)

def get_server_config(self) -> ServerCommandConfig:
def get_server_config(
self, environment: SDocRuntimeEnvironment
) -> ServerCommandConfig:
assert isinstance(environment, SDocRuntimeEnvironment), environment
return ServerCommandConfig(
environment=environment,
input_path=self.args.input_path,
output_path=self.args.output_path,
reload=self.args.reload,
Expand Down
66 changes: 32 additions & 34 deletions strictdoc/cli/main.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
# pylint: disable=wrong-import-position
# flake8: noqa: E402

import os
import sys

try:
strictdoc_root_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "..")
)
if not os.path.isdir(strictdoc_root_path):
raise FileNotFoundError
sys.path.append(strictdoc_root_path)

from strictdoc import STRICTDOC_ROOT_PATH
from strictdoc.cli.cli_arg_parser import (
create_sdoc_args_parser,
ExportCommandConfig,
PassthroughCommandConfig,
DumpGrammarCommandConfig,
ImportExcelCommandConfig,
ImportReqIFCommandConfig,
)
from strictdoc.commands.about_command import AboutCommand
from strictdoc.commands.dump_grammar_command import DumpGrammarCommand
from strictdoc.commands.version_command import VersionCommand
from strictdoc.core.actions.export_action import ExportAction
from strictdoc.core.actions.import_action import ImportAction
from strictdoc.core.actions.passthrough_action import PassthroughAction
from strictdoc.core.project_config import ProjectConfig, ProjectConfigLoader
from strictdoc.helpers.parallelizer import Parallelizer
from strictdoc.server.server import run_strictdoc_server

except FileNotFoundError:
print("error: could not locate strictdoc's root folder.")
sys.exit(1)
strictdoc_root_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "..")
)
if not os.path.isdir(strictdoc_root_path):
raise FileNotFoundError
sys.path.append(strictdoc_root_path)

from strictdoc import environment
from strictdoc.cli.cli_arg_parser import (
create_sdoc_args_parser,
ExportCommandConfig,
PassthroughCommandConfig,
DumpGrammarCommandConfig,
ImportExcelCommandConfig,
ImportReqIFCommandConfig,
)
from strictdoc.commands.about_command import AboutCommand
from strictdoc.commands.dump_grammar_command import DumpGrammarCommand
from strictdoc.commands.version_command import VersionCommand
from strictdoc.core.actions.export_action import ExportAction
from strictdoc.core.actions.import_action import ImportAction
from strictdoc.core.actions.passthrough_action import PassthroughAction
from strictdoc.core.project_config import ProjectConfig, ProjectConfigLoader
from strictdoc.helpers.parallelizer import Parallelizer
from strictdoc.server.server import run_strictdoc_server


def _main(parallelizer):
Expand All @@ -57,7 +55,7 @@ def _main(parallelizer):

elif parser.is_export_command:
config: ExportCommandConfig = parser.get_export_config(
STRICTDOC_ROOT_PATH
environment=environment
)
project_config: ProjectConfig = (
ProjectConfigLoader.load_from_path_or_get_default(
Expand All @@ -74,7 +72,7 @@ def _main(parallelizer):
export_action.export()

elif parser.is_server_command:
server_config = parser.get_server_config()
server_config = parser.get_server_config(environment=environment)
project_config: ProjectConfig = (
ProjectConfigLoader.load_from_path_or_get_default(
path_to_config_dir=server_config.input_path
Expand All @@ -86,14 +84,14 @@ def _main(parallelizer):

elif parser.is_import_command_reqif:
import_config: ImportReqIFCommandConfig = (
parser.get_import_config_reqif(STRICTDOC_ROOT_PATH)
parser.get_import_config_reqif(environment.path_to_strictdoc)
)
import_action = ImportAction()
import_action.do_import(import_config)

elif parser.is_import_command_excel:
import_config: ImportExcelCommandConfig = (
parser.get_import_config_excel(STRICTDOC_ROOT_PATH)
parser.get_import_config_excel(environment.path_to_strictdoc)
)
import_action = ImportAction()
import_action.do_import(import_config)
Expand Down
104 changes: 104 additions & 0 deletions strictdoc/core/environment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import os
import sys


class SDocRuntimeEnvironment:
def __init__(self, path_to_init: str):
assert isinstance(path_to_init, str), path_to_init

# FIXME: Delete me.
self.path_to_init = path_to_init
# https://stackoverflow.com/a/55556360/598057
self.is_nuitka = "__compiled__" in globals()
self.is_py_installer = (
getattr(sys, "frozen", False) and not self.is_nuitka
)

# "frozen" attribute is set to True by PyInstaller, and it looks like
# Nuitka does the same.
self.is_binary_dist: bool = (
getattr(sys, "frozen", False) or self.is_nuitka
)

if self.is_binary_dist:
# When it is a binary distribution, we don't have a Python package
# file structure but only a binary with surrounding libraries and
# data files (Jinja HTML files, CSS, JS, ...).
path_to_main = os.path.abspath(sys.argv[0])
path_to_main_dir = os.path.dirname(os.path.abspath(path_to_main))

# Nuitka
if self.is_nuitka:
self.path_to_strictdoc = path_to_main_dir
# PyInstaller
else:
self.path_to_strictdoc = (
sys._MEIPASS # pylint: disable=protected-access, no-member
)
else:
self.path_to_strictdoc = os.path.abspath(
os.path.join(path_to_init, "..", "..")
)
if not os.path.isdir(self.path_to_strictdoc):
raise FileNotFoundError(self.path_to_strictdoc)
if not os.path.isabs(self.path_to_strictdoc):
raise EnvironmentError(
"Path to strictdoc's package path must be an absolute path: "
f"{self.path_to_strictdoc}"
)

def get_static_files_path(self):
if self.is_binary_dist:
return os.path.join(self.path_to_strictdoc, "_static")
return os.path.join(
self.path_to_strictdoc, "strictdoc/export/html/_static"
)

def get_extra_static_files_path(self):
if self.is_binary_dist:
return os.path.join(self.path_to_strictdoc, "_static_extra")
return os.path.join(
self.path_to_strictdoc, "strictdoc/export/html/_static_extra"
)

def get_path_to_rst_templates(self):
if self.is_py_installer:
# If the application is run as a bundle, the PyInstaller bootloader
# extends the sys module by a flag frozen=True and sets the app
# path into variable _MEIPASS'.
bundle_dir = (
sys._MEIPASS # pylint: disable=protected-access, no-member
)
return os.path.join(bundle_dir, "templates/rst")
if self.is_nuitka:
return os.path.join(self.path_to_strictdoc, "templates/rst")
# Normal Python
return os.path.join(
self.path_to_strictdoc, "strictdoc", "export", "rst", "templates"
)

def get_path_to_html_templates(self):
if self.is_py_installer:
# If the application is run as a bundle, the PyInstaller bootloader
# extends the sys module by a flag frozen=True and sets the app
# path into variable _MEIPASS'.
bundle_dir = (
sys._MEIPASS # pylint: disable=protected-access, no-member
)
return os.path.join(bundle_dir, "templates/html")
if self.is_nuitka:
return os.path.join(self.path_to_strictdoc, "templates/html")
# Normal Python
path_to_html_templates = os.path.join(
self.path_to_strictdoc, "strictdoc", "export", "html", "templates"
)
assert os.path.isdir(path_to_html_templates), path_to_html_templates
assert os.path.isabs(path_to_html_templates), path_to_html_templates
return path_to_html_templates

def get_path_to_export_html(self):
if self.is_nuitka:
return self.path_to_strictdoc
return os.path.join(
self.path_to_strictdoc, "strictdoc", "export", "html"
)
8 changes: 0 additions & 8 deletions strictdoc/core/project_config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import pickle

import toml

Expand All @@ -10,13 +9,6 @@ class ProjectConfig:
def __init__(self, project_title: str):
self.project_title: str = project_title

def dump_to_string(self) -> str:
return pickle.dumps(self, 0).decode(encoding="utf8")

@staticmethod
def config_from_string_dump(dump: str) -> "ProjectConfig":
return pickle.loads(dump.encode(encoding="utf8"))

@staticmethod
def default_config():
return ProjectConfig(project_title=ProjectConfig.DEFAULT_PROJECT_TITLE)
Expand Down
17 changes: 3 additions & 14 deletions strictdoc/export/html/html_templates.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
import os.path
import sys
from jinja2 import Environment, StrictUndefined, FileSystemLoader

from jinja2 import Environment, PackageLoader, StrictUndefined, FileSystemLoader


def _get_package_loader():
if getattr(sys, "frozen", False):
# If the application is run as a bundle, the PyInstaller bootloader
# extends the sys module by a flag frozen=True and sets the app
# path into variable _MEIPASS'.
bundle_dir = sys._MEIPASS # pylint: disable=protected-access
return FileSystemLoader(os.path.join(bundle_dir, "templates/html"))
return PackageLoader("strictdoc", "export/html/templates")
from strictdoc import environment


class HTMLTemplates:
jinja_environment = Environment(
loader=_get_package_loader(),
loader=FileSystemLoader(environment.get_path_to_html_templates()),
undefined=StrictUndefined,
)
# TODO: Check if this line is still needed (might be some older workaround).
Expand Down
Loading