Skip to content

Commit fb6cb27

Browse files
committed
Move resources/ into postforge package for pip-installability
Resources (fonts, init scripts, encodings, device definitions) now live inside the postforge/ package so they are included in wheels and sdists. Path resolution uses package-relative lookups instead of os.chdir() to the project root, preserving the user's working directory. - git mv resources/ postforge/resources/ - Replace ProjectDir with PackageDir in system params - Add package directory search in _resolve_filename() - Remove os.chdir() from cli_runner.py - VMDir uses tempfile.mkdtemp() (can't write into site-packages) - Add resources/**/* to pyproject.toml package-data
1 parent 6b50376 commit fb6cb27

64 files changed

Lines changed: 35 additions & 37 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

postforge/cli.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,11 @@ def main() -> int:
7171

7272
# Remember user's working directory — file paths on the command line
7373
# and in interactive mode should resolve relative to where pf was invoked
74-
script_dir = os.path.dirname(os.path.abspath(__file__))
75-
project_dir = os.path.dirname(script_dir)
74+
package_dir = os.path.dirname(os.path.abspath(__file__)) # postforge/
7675
user_cwd = os.getcwd()
7776

7877
# Get available devices from OutputDevice directory
79-
device_dir = os.path.join(project_dir, "resources", "OutputDevice")
78+
device_dir = os.path.join(package_dir, "resources", "OutputDevice")
8079

8180
available_devices = []
8281
if os.path.exists(device_dir):
@@ -111,8 +110,7 @@ def main() -> int:
111110
print(f"System font cache rebuilt: {cache.font_count()} fonts found")
112111
return 0
113112

114-
# Resolve input file paths to absolute (relative to user's CWD) before
115-
# we chdir to the project root for PS resource file resolution.
113+
# Resolve input file paths to absolute (relative to user's CWD)
116114
inputfiles = [f if f == "-" else (os.path.join(user_cwd, f) if not os.path.isabs(f) else f)
117115
for f in args.inputfiles]
118116
device = args.device
@@ -155,7 +153,7 @@ def main() -> int:
155153
inputfiles = [stdin_temp.name if f == "-" else f for f in inputfiles]
156154

157155
try:
158-
return run(args, inputfiles, stdin_temp, user_cwd, project_dir,
156+
return run(args, inputfiles, stdin_temp, user_cwd, package_dir,
159157
available_devices, device, memory_profile,
160158
gc_analysis, leak_analysis, performance_profile,
161159
profile_type, profile_output, page_filter)

postforge/cli_args.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def get_output_base_name(outputfile: str, inputfiles: list) -> str:
9191
def _get_version() -> str:
9292
"""Read the PostForge version from sysdict.ps (sole source of truth)."""
9393
sysdict = os.path.join(os.path.dirname(os.path.abspath(__file__)),
94-
os.pardir, "resources", "Init", "sysdict.ps")
94+
"resources", "Init", "sysdict.ps")
9595
with open(sysdict, "r") as f:
9696
for line in f:
9797
m = re.search(r'/revisionstring\s+\(([^)]+)\)', line)

postforge/cli_runner.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,7 @@ def _conditional_paint(ctxt, elem):
222222
# Set output naming parameters in page device
223223
if ctxt.gstate.page_device:
224224
base_name = get_output_base_name(args.outputfile, inputfiles)
225-
# Make output dir absolute relative to user's original CWD,
226-
# since PS execution runs with CWD set to the project root.
225+
# Make output dir absolute relative to user's CWD
227226
output_dir = args.output_dir
228227
if not os.path.isabs(output_dir):
229228
output_dir = os.path.join(user_cwd, output_dir)
@@ -478,7 +477,7 @@ def _run_interactive(ctxt, memory_profile, performance_profile, perf_profiler):
478477
print("\n" + ps_memory.generate_memory_report())
479478

480479

481-
def run(args, inputfiles, stdin_temp, user_cwd, project_dir,
480+
def run(args, inputfiles, stdin_temp, user_cwd, package_dir,
482481
available_devices, device, memory_profile, gc_analysis,
483482
leak_analysis, performance_profile, profile_type,
484483
profile_output, page_filter):
@@ -492,7 +491,7 @@ def run(args, inputfiles, stdin_temp, user_cwd, project_dir,
492491
inputfiles: List of resolved input file paths.
493492
stdin_temp: Temporary file for stdin input (or None).
494493
user_cwd: User's original working directory.
495-
project_dir: PostForge project root directory.
494+
package_dir: PostForge package directory (postforge/).
496495
available_devices: List of available device names.
497496
device: Explicitly requested device name (or None).
498497
memory_profile: Whether memory profiling is enabled.
@@ -529,20 +528,14 @@ def run(args, inputfiles, stdin_temp, user_cwd, project_dir,
529528
# Initialize the global system params
530529
system_params = init_system_params()
531530

532-
# Set CWD to project root for the entire session. PS resource files
533-
# (fonts, devices, encodings) use relative paths like resources/Font/...
534-
# that must resolve from the project root. Input file paths and output
535-
# directories are made absolute (relative to user_cwd) before use.
536-
os.chdir(project_dir)
537531
ctxt, err_string = create_context(system_params)
538532

539533
if err_string:
540534
print(err_string)
541535
quit()
542536

543537
# Store user's original CWD so file operators (run, file, etc.) can
544-
# resolve relative paths against where the user invoked PostForge,
545-
# not the project root we chdir'd to above.
538+
# resolve relative paths against where the user invoked PostForge.
546539
ctxt.user_cwd = user_cwd
547540

548541
# Take memory snapshot after context initialization

postforge/core/context_init.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import os
1313
import random
1414
import sys
15+
import tempfile
1516
import time
1617
from typing import Any, Dict, Optional, Tuple
1718

@@ -35,33 +36,30 @@ def init_system_params() -> Dict[str, Any]:
3536
3637
Returns:
3738
Dict[str, Any]: System parameters dictionary containing:
39+
- PackageDir: Path to the postforge package directory
3840
- ByteOrder: Boolean indicating byte order (True for big-endian)
3941
- CurrDisplayList: Current display list index
4042
- FontResourceDir: Path to font resources
41-
- GenericResourceDir: Path to generic resources
4243
- OutputDeviceResourceDir: Path to output device definitions
43-
- VMDir: Path for virtual memory storage
44+
- VMDir: Path for virtual memory storage (temp directory)
4445
- PageCount: Initial page count
4546
- PrinterName: Default printer name
4647
- RealFormat: Real number format ('IEE')
4748
- Revision: PostScript language revision level
4849
"""
4950

50-
# Get the directory containing the postforge package
51-
script_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
52-
# Get the parent directory (main project directory)
53-
project_dir = os.path.dirname(script_dir)
51+
# Get the postforge package directory (postforge/)
52+
package_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
5453

5554
return {
56-
"ProjectDir": project_dir,
55+
"PackageDir": package_dir,
5756
"ByteOrder": True,
5857
"CurrDisplayList": 0,
59-
"FontResourceDir": os.path.join(project_dir, "resources", "Font"),
60-
"GenericResourceDir": os.path.join(project_dir, "generic_resources"),
58+
"FontResourceDir": os.path.join(package_dir, "resources", "Font"),
6159
"OutputDeviceResourceDir": os.path.join(
62-
project_dir, "resources", "OutputDevice"
60+
package_dir, "resources", "OutputDevice"
6361
),
64-
"VMDir": os.path.join(project_dir, "vm"),
62+
"VMDir": tempfile.mkdtemp(prefix="postforge_vm_"),
6563
"PageCount": 0,
6664
"PrinterName": "PostForge",
6765
"RealFormat": "IEE",
@@ -276,7 +274,7 @@ def create_context(
276274
#
277275
# add the filename to ctxt.strings
278276
# Use absolute path so PostForge works from any working directory
279-
file_name = os.path.join(system_params["ProjectDir"], "resources", "Init", "sysdict.ps")
277+
file_name = os.path.join(system_params["PackageDir"], "resources", "Init", "sysdict.ps")
280278
# Use forward slashes — backslashes are PS escape characters
281279
# (\r in \resources becomes carriage return, corrupting the path)
282280
file_name = file_name.replace("\\", "/")

postforge/operators/file.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616
def _resolve_filename(ctxt, filename):
1717
"""Resolve a relative filename against likely directories.
1818
19-
PostForge changes CWD to the project root for internal resource resolution,
20-
so relative paths in user PS files won't resolve against the user's directory.
21-
This helper tries multiple locations in order:
19+
Tries multiple locations in order:
2220
23-
1. As-is (works for absolute paths and project-relative resource files)
24-
2. Relative to the directory of the currently executing file (e_stack walk)
25-
3. Relative to the user's original working directory
21+
1. As-is (works for absolute paths and files relative to CWD)
22+
2. Relative to the package directory (resolves resources/Init/... etc.)
23+
3. Relative to the directory of the currently executing file (e_stack walk)
24+
4. Relative to the user's original working directory (safety net)
2625
2726
Args:
2827
ctxt: PostScript execution context.
@@ -35,6 +34,13 @@ def _resolve_filename(ctxt, filename):
3534
if os.path.isabs(filename) or os.path.exists(filename):
3635
return filename
3736

37+
# Try relative to the package directory (where postforge/ lives)
38+
package_dir = ctxt.system_params.get("PackageDir")
39+
if package_dir:
40+
candidate = os.path.join(package_dir, filename)
41+
if os.path.exists(candidate):
42+
return candidate
43+
3844
# Try relative to the directory of the currently executing file
3945
for item in reversed(ctxt.e_stack):
4046
if isinstance(item, (ps.File, ps.Run)):
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)