Skip to content

Commit

Permalink
Work towards satisfying pylint, part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
wallabra committed Aug 24, 2023
1 parent ad3df8e commit 50d9b3b
Show file tree
Hide file tree
Showing 26 changed files with 323 additions and 303 deletions.
Binary file modified example/out/ZDWorld-2.13.6-zdw1-debug-asset.pk3
Binary file not shown.
Binary file modified example/out/ZDWorld-2.13.6-zdw1-debug-code.pk3
Binary file not shown.
Binary file modified example/out/ZDWorld-2.13.6-zdw1-release-asset.pk3
Binary file not shown.
Binary file modified example/out/ZDWorld-2.13.6-zdw1-release-code.pk3
Binary file not shown.
Binary file modified example/out/ZDWorld-2.13.6-zdw1-release-foes-asset.pk3
Binary file not shown.
Binary file modified example/out/ZDWorld-2.13.6-zdw1-release-foes-code.pk3
Binary file not shown.
12 changes: 12 additions & 0 deletions zdcode/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
"""ZDCode is a language which compilers to DECORATE.
DECORATE is a legacy scripting language used by the ZDoom family of source ports. While
modern source ports support ZScript, a more advanced scripting language which is much
more powerful and superior to both DECORATE and ZDCode, using DECORATE allows other
source ports to be supported by a mod as well. ZDCode makes the process of writing
DECORATE code less tedious,
This project is available in the MIT License. For more info, see the LICENSE.md file.
(c)2023 Gustavo Ramos Rehermann and contributors."""

__VERSION__ = "2.13.3"

import collections
Expand Down
1 change: 1 addition & 0 deletions zdcode/__main__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""The main entry point for Python."""
from . import program

program.main()
65 changes: 55 additions & 10 deletions zdcode/bundle.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"""Handles the bundling of multiple outputs.
Used by Zake to produce the output artifacts."""
import fnmatch
import functools
import heapq
Expand All @@ -8,6 +11,7 @@
import typing
import zipfile
from collections import deque
from typing import Callable

import attr

Expand All @@ -17,19 +21,26 @@
@functools.total_ordering
@attr.s(order=False)
class BundleOutput:
"""Holds information on an output and what should be used to construct it.
This is somewhat similar to a stripped down Make rule."""

name: str = attr.ib()
output: str = attr.ib()
priority: float = attr.ib(default=1.0)
matchers: list[str] = attr.ib(factory=list)
excluders: list[str] = attr.ib(factory=list)

def add_matcher(self, matcher: str):
"""Adds a file path matcher."""
self.matchers.append(matcher)

def add_excluder(self, excluder: str):
"""Adds a file path excluder."""
self.excluders.append(excluder)

def matches(self, rel_path: str) -> bool:
"""Returns whether a file path matches this output's criteria."""
for excluder in self.excluders:
if fnmatch.fnmatch(rel_path, excluder):
return False
Expand All @@ -55,11 +66,13 @@ def __eq__(self, other: object) -> bool:


class BundleDependencyError(Exception):
pass
"""An error relating to Zake's dependency resolution."""


@attr.s
class BundleInputWalker:
"""Walks file trees specified by a Zake project as relevant to a build process."""

code: ZDCode = attr.ib()
bundle: "Bundle" = attr.ib()
deps: list[tuple[pathlib.Path, pathlib.PurePath]] = attr.ib(factory=list)
Expand All @@ -72,8 +85,14 @@ class BundleInputWalker:
collected: typing.Deque[tuple[str, str, bytes]] = attr.ib(factory=list)

@classmethod
def new(self, bundle, error_handler=None, preproc_defs=None) -> "BundleInputWalker":
return BundleInputWalker(
def new(
cls,
bundle: "Bundle",
error_handler: Callable[[str], None] = None,
preproc_defs: dict[str, str] = None,
) -> "BundleInputWalker":
"""Makes a walker for a bundle."""
return cls(
bundled=set(),
deps=[],
code=ZDCode(),
Expand All @@ -85,14 +104,17 @@ def new(self, bundle, error_handler=None, preproc_defs=None) -> "BundleInputWalk
)

def add_dep(self, url: pathlib.Path, target: pathlib.PurePath) -> None:
"""Registers a dependency to be later resolved."""
self.deps.append((url, target))

def scan_deps(self):
"""Scan the registered dependencies."""
while self.deps:
mod, target = self.deps.pop()
self.scan_dep(mod, target)

def build(self) -> typing.Optional[tuple[int, str]]:
"""Perform the build tasks."""
while self.build_tasks:
task = self.build_tasks.pop()

Expand All @@ -107,6 +129,7 @@ def build(self) -> typing.Optional[tuple[int, str]]:
return None

def store_collected(self, out_zip: zipfile.ZipFile, target: str, data: bytes):
"""Store the collected build and bundle results in an output path."""
# check if file already exists in target;
# if so, add extension

Expand All @@ -124,6 +147,8 @@ def store_collected(self, out_zip: zipfile.ZipFile, target: str, data: bytes):
out_zip.writestr(out_path, data)

def assemble(self):
"""Internally assemble a zip file to then store at an output path."""

zips: dict[str, zipfile.ZipFile] = {}
zipfiles = set()

Expand All @@ -139,7 +164,12 @@ def assemble(self):
out_zip = zips[oname.lower()]
self.store_collected(out_zip, target, data)

for zipf in zips.values():
zipf.close()

def scan_dep(self, url: pathlib.Path, target: pathlib.PurePath):
"""Scan for a single dependency."""

url_str = str(url)

if url_str in self.bundled:
Expand All @@ -151,6 +181,7 @@ def scan_dep(self, url: pathlib.Path, target: pathlib.PurePath):
def scan_dep_url(
self, url: pathlib.Path, target: pathlib.PurePath, relative: pathlib.PurePath
):
"""Scan a dependency which is a path."""
if url.is_dir():
self.scan_dep_dir(url, target, relative)

Expand All @@ -160,6 +191,7 @@ def scan_dep_url(
def scan_dep_zip(
self, url: pathlib.Path, target: pathlib.PurePath, relative: pathlib.PurePath
):
"""Scan a dependency which is a ZIP file."""
with tempfile.TemporaryDirectory() as extract_out:
extractdest = pathlib.Path(extract_out)

Expand All @@ -171,6 +203,7 @@ def scan_dep_zip(
def scan_dep_file(
self, url: pathlib.Path, target: pathlib.PurePath, relative: pathlib.PurePath
):
"""Scan a dependency which is a file."""
if url.stem.split(".")[0].upper() == "ZDCODE":
self.build_tasks.append(self._compile_task(url))

Expand All @@ -182,12 +215,15 @@ def scan_dep_file(
self.collect(opath, url.read_bytes())

def collect(self, out_path: pathlib.PurePath, data: bytes):
"""Collect the output of the bundling process."""
output = self.bundle.find_output(out_path.name)

if output:
self.collected.append((str(out_path), output.name.lower(), data))

def _compile_task(self, zdc):
"""Decorates a function into a compilation task."""

def compile_mod_zdcode():
with zdc.open() as zdc_fp:
return self.code.add(
Expand All @@ -203,6 +239,8 @@ def compile_mod_zdcode():
def scan_dep_dir(
self, url: pathlib.Path, target: pathlib.PurePath, relative: pathlib.PurePath
):
"""Scan a dependency which is a directory."""

for filepath in url.rglob("*"):
if filepath.is_file() and filepath.name != "DEPINDEX":
self.scan_dep_file(
Expand All @@ -214,24 +252,28 @@ def scan_dep_dir(
if indx_path.is_file():
lines = indx_path.read().splitlines()

for l in lines:
l = l.strip()
for line in lines:
line = line.strip()

if l:
dep_path = url.parent / l
if line:
dep_path = url.parent / line

if not dep_path.exists():
dep_path = pathlib.Path.cwd() / l
dep_path = pathlib.Path.cwd() / line

if not dep_path.exists():
raise BundleDependencyError(
"The file {} depends on {}, which does not exist, neither as a sibling of the dependent's dir, nor under the working one!"
"The file {} depends on {}, which does not exist, "
"neither as a sibling of the dependent's dir, "
"nor under the working one!"
)

self.deps.append(dep_path)


class Bundle:
"""A bundling process. Can have multiple outputs."""

def __init__(self, *mods: list[tuple[str, str]], outputs=None, error_handler=None):
self.mods = list(mods)
self.error_handler = error_handler
Expand All @@ -242,6 +284,7 @@ def __init__(self, *mods: list[tuple[str, str]], outputs=None, error_handler=Non
self.output_heap.sort()

def add_output(self, name: str, output: str, priority: float = 1.0) -> BundleOutput:
"""Creates and registers a new output for this bundle."""
res = self.outputs.setdefault(
name, BundleOutput(name=name, output=output, priority=priority)
)
Expand All @@ -253,6 +296,7 @@ def add_output(self, name: str, output: str, priority: float = 1.0) -> BundleOut
return res

def find_output(self, rel_path: str) -> typing.Optional[BundleOutput]:
"""Finds a [BundleOutput] in this bundle by name."""
for bundle in self.output_heap:
if bundle.matches(rel_path):
return bundle
Expand All @@ -264,6 +308,7 @@ def bundle(
error_handler=None,
preproc_defs=(),
):
"""Executes the bundling process."""
walker = BundleInputWalker.new(
error_handler=error_handler or self.error_handler,
preproc_defs=dict(preproc_defs),
Expand All @@ -285,7 +330,7 @@ def bundle(
return err

# count files bundled
print("Collected {} files.".format(len(walker.collected)))
print(f"Collected {len(walker.collected)} files.")

# assemble outputs
print("Assembling...")
Expand Down
Loading

0 comments on commit 50d9b3b

Please sign in to comment.