Replace rsrcfork.open's rsrcfork parameter with a more usable version

The new fork parameter accepts strings, which are more understandable
than the old None/True/False values, and can be extended in the future.
This commit is contained in:
dgelessus 2019-08-31 20:07:26 +02:00
parent c4fe09dbf0
commit e6779b021a
3 changed files with 32 additions and 9 deletions

View File

@ -144,6 +144,10 @@ Version 1.2.0 (next version)
* This fixes reading certain system files with resource data in their data fork (such as HIToolbox.rsrc in HIToolbox.framework, or .dfont fonts) on recent macOS versions (at least macOS 10.14, possibly earlier). Although these files have no resource fork, recent macOS versions will successfully open the resource fork and return garbage data for it. This behavior is now detected and handled by using the data fork instead.
* Replaced the ``rsrcfork`` parameter of ``rsrcfork.open``/``ResourceFork.open`` with a new ``fork`` parameter. ``fork`` accepts string values (like the command line ``--fork`` option) rather than ``rsrcfork``'s hard to understand ``None``/``True``/``False``.
* The old ``rsrcfork`` parameter has been deprecated and will be removed in the future, but for now it still works as before.
* Added an explanatory message when a resource filter on the command line doesn't match any resources in the resource file. Previously there would either be no output or a confusing error, depending on the selected ``--format``.
* Improved error messages when attempting to read multiple resources using ``--format=hex`` or ``--format=raw``.
* Fixed reading from non-seekable streams not working for some resource files.

View File

@ -196,8 +196,6 @@ def main():
ns = ap.parse_args()
ns.fork = {"auto": None, "data": False, "rsrc": True}[ns.fork]
if ns.file == "-":
if ns.fork is not None:
print("Cannot specify an explicit fork when reading from stdin", file=sys.stderr)
@ -205,7 +203,7 @@ def main():
rf = api.ResourceFile(sys.stdin.buffer)
else:
rf = api.ResourceFile.open(ns.file, rsrcfork=ns.fork)
rf = api.ResourceFile.open(ns.file, fork=ns.fork)
with rf:
if ns.header_system or ns.header_application:

View File

@ -5,6 +5,7 @@ import io
import os
import struct
import typing
import warnings
from . import compress
@ -199,11 +200,16 @@ class ResourceFile(collections.abc.Mapping):
return f"<{type(self).__module__}.{type(self).__qualname__} at {id(self):#x} containing {len(self)} resources with IDs: {list(self)}>"
@classmethod
def open(cls, filename: typing.Union[str, bytes, os.PathLike], *, rsrcfork: typing.Optional[bool]=None, **kwargs) -> "ResourceFile":
def open(cls, filename: typing.Union[str, bytes, os.PathLike], *, fork: str="auto", **kwargs) -> "ResourceFile":
"""Open the file at the given path as a ResourceFile.
If rsrcfork is not None, it is treated as boolean and controls whether the data or resource fork of the file should be opened. (On systems other than macOS, opening resource forks will not work of course, since they don't exist.)
If rsrcfork is None, the resource fork is opened if it exists and contains valid resource data, otherwise the data fork is opened instead.
The fork parameter controls which fork of the file the resource data will be read from. It accepts the following values:
* "auto" (the default): Automatically select the correct fork. The resource fork will be used if the file has one and it contains valid resource data. Otherwise the data fork will be used.
* "rsrc": Force use of the resource fork and never fall back to the data fork. This will not work on systems other than macOS, because they do not support resource forks natively.
* "data": Force use of the data fork, even if a resource fork is present.
The rsrcfork parameter is deprecated and will be removed in the future. It has the same purpose as the fork parameter, but accepts different argument values: None stands for "auto", True stands for "rsrc", and False stands for "data". These argument values are less understandable than the string versions and are not easily extensible in the future, which is why the parameter has been deprecated.
"""
if "close" in kwargs:
@ -211,7 +217,20 @@ class ResourceFile(collections.abc.Mapping):
kwargs["close"] = True
if rsrcfork is None:
if "rsrcfork" in kwargs:
if fork != "auto":
raise TypeError("The fork and rsrcfork parameters cannot be used together. Please use only the fork parameter; it replaces the deprecated rsrcfork parameter.")
if kwargs["rsrcfork"] is None:
fork = "auto"
elif kwargs["rsrcfork"]:
fork = "rsrc"
else:
fork = "data"
warnings.warn(DeprecationWarning(f"The rsrcfork parameter has been deprecated and will be removed in a future version. Please use fork={fork!r} instead of rsrcfork={kwargs['rsrcfork']!r}."))
del kwargs["rsrcfork"]
if fork == "auto":
# Determine whether the file has a usable resource fork.
try:
# Try to open the resource fork.
@ -231,12 +250,14 @@ class ResourceFile(collections.abc.Mapping):
except BaseException:
f.close()
raise
elif rsrcfork:
elif fork == "rsrc":
# Force use of the resource fork.
return cls(open(os.path.join(filename, "..namedfork", "rsrc"), "rb"), **kwargs)
else:
elif fork == "data":
# Force use of the data fork.
return cls(open(filename, "rb"), **kwargs)
else:
raise ValueError(f"Unsupported value for the fork parameter: {fork!r}")
def __init__(self, stream: typing.io.BinaryIO, *, close: bool=False):
"""Create a ResourceFile wrapping the given byte stream.