resourceutil.py
145 lines
| 4.7 KiB
| text/x-python
|
PythonLexer
Martin von Zweigbergk
|
r44067 | # resourceutil.py - utility for looking up resources | ||
# | ||||
# Copyright 2005 K. Thananchayan <thananck@yahoo.com> | ||||
Raphaël Gomès
|
r47575 | # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com> | ||
Martin von Zweigbergk
|
r44067 | # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> | ||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
Matt Harbison
|
r52756 | from __future__ import annotations | ||
Martin von Zweigbergk
|
r44067 | |||
Martin von Zweigbergk
|
r44068 | import os | ||
Martin von Zweigbergk
|
r44067 | import sys | ||
Matt Harbison
|
r52562 | import typing | ||
Martin von Zweigbergk
|
r44067 | |||
Mads Kiilerich
|
r53004 | from typing import Iterator | ||
Martin von Zweigbergk
|
r44067 | from .. import pycompat | ||
Matt Harbison
|
r52562 | if typing.TYPE_CHECKING: | ||
from typing import ( | ||||
BinaryIO, | ||||
Iterator, | ||||
) | ||||
Martin von Zweigbergk
|
r44067 | def mainfrozen(): | ||
"""return True if we are a frozen executable. | ||||
The code supports py2exe (most common, Windows only) and tools/freeze | ||||
(portable, not much used). | ||||
""" | ||||
return ( | ||||
r51821 | hasattr(sys, "frozen") # new py2exe | |||
or hasattr(sys, "importers") # old py2exe | ||||
Mads Kiilerich
|
r53003 | or getattr( | ||
getattr(sys.modules.get('__main__'), '__spec__', None), | ||||
'origin', | ||||
None, | ||||
) | ||||
== 'frozen' # tools/freeze | ||||
Manuel Jacob
|
r45476 | ) | ||
Martin von Zweigbergk
|
r44068 | |||
# the location of data files matching the source code | ||||
Augie Fackler
|
r44723 | if mainfrozen() and getattr(sys, "frozen", None) != "macosx_app": | ||
Martin von Zweigbergk
|
r44068 | # executable version (py2exe) doesn't support __file__ | ||
datapath = os.path.dirname(pycompat.sysexecutable) | ||||
Matt Harbison
|
r44678 | _rootpath = datapath | ||
Matt Harbison
|
r44706 | |||
# The installers store the files outside of library.zip, like | ||||
# C:\Program Files\Mercurial\defaultrc\*.rc. This strips the | ||||
# leading "mercurial." off of the package name, so that these | ||||
# pseudo resources are found in their directory next to the | ||||
# executable. | ||||
Matt Harbison
|
r52562 | def _package_path(package: bytes) -> bytes: | ||
Augie Fackler
|
r44723 | dirs = package.split(b".") | ||
assert dirs[0] == b"mercurial" | ||||
Matt Harbison
|
r44706 | return os.path.join(_rootpath, *dirs[1:]) | ||
Martin von Zweigbergk
|
r44068 | else: | ||
datapath = os.path.dirname(os.path.dirname(pycompat.fsencode(__file__))) | ||||
Matt Harbison
|
r44479 | _rootpath = os.path.dirname(datapath) | ||
Martin von Zweigbergk
|
r44323 | |||
Matt Harbison
|
r52562 | def _package_path(package: bytes) -> bytes: | ||
Augie Fackler
|
r44723 | return os.path.join(_rootpath, *package.split(b".")) | ||
Matt Harbison
|
r44706 | |||
Martin von Zweigbergk
|
r44323 | try: | ||
Martin von Zweigbergk
|
r46024 | # importlib.resources exists from Python 3.7; see fallback in except clause | ||
# further down | ||||
Matt Harbison
|
r48821 | from importlib import resources # pytype: disable=import-error | ||
Martin von Zweigbergk
|
r44323 | |||
# Force loading of the resources module | ||||
Mads Kiilerich
|
r53004 | if hasattr(resources, 'files'): # Introduced in Python 3.9 | ||
r50838 | resources.files # pytype: disable=module-attr | |||
else: | ||||
resources.open_binary # pytype: disable=module-attr | ||||
Martin von Zweigbergk
|
r44323 | |||
Matt Harbison
|
r49965 | # py2exe raises an AssertionError if uses importlib.resources | ||
if getattr(sys, "frozen", None) in ("console_exe", "windows_exe"): | ||||
raise ImportError | ||||
Martin von Zweigbergk
|
r44407 | except (ImportError, AttributeError): | ||
Martin von Zweigbergk
|
r46024 | # importlib.resources was not found (almost definitely because we're on a | ||
# Python version before 3.7) | ||||
Martin von Zweigbergk
|
r44323 | |||
Matt Harbison
|
r52562 | def open_resource(package: bytes, name: bytes) -> "BinaryIO": | ||
Martin von Zweigbergk
|
r44323 | path = os.path.join(_package_path(package), name) | ||
Augie Fackler
|
r44723 | return open(path, "rb") | ||
Matt Harbison
|
r44480 | |||
Matt Harbison
|
r52562 | def is_resource(package: bytes, name: bytes) -> bool: | ||
Matt Harbison
|
r44480 | path = os.path.join(_package_path(package), name) | ||
try: | ||||
return os.path.isfile(pycompat.fsdecode(path)) | ||||
except (IOError, OSError): | ||||
return False | ||||
Matt Harbison
|
r44481 | |||
Matt Harbison
|
r52562 | def contents(package: bytes) -> "Iterator[bytes]": | ||
Matt Harbison
|
r44481 | path = pycompat.fsdecode(_package_path(package)) | ||
for p in os.listdir(path): | ||||
yield pycompat.fsencode(p) | ||||
r48668 | ||||
else: | ||||
from .. import encoding | ||||
Matt Harbison
|
r52562 | def open_resource(package: bytes, name: bytes) -> "BinaryIO": | ||
r51821 | if hasattr(resources, 'files'): | |||
r50838 | return ( | |||
resources.files( # pytype: disable=module-attr | ||||
pycompat.sysstr(package) | ||||
) | ||||
.joinpath(pycompat.sysstr(name)) | ||||
.open('rb') | ||||
) | ||||
else: | ||||
return resources.open_binary( # pytype: disable=module-attr | ||||
pycompat.sysstr(package), pycompat.sysstr(name) | ||||
) | ||||
r48668 | ||||
Mads Kiilerich
|
r53004 | def is_resource(package: bytes, name: bytes) -> bool: | ||
if hasattr(resources, 'files'): # Introduced in Python 3.9 | ||||
return ( | ||||
resources.files(pycompat.sysstr(package)) | ||||
.joinpath(encoding.strfromlocal(name)) | ||||
.is_file() | ||||
) | ||||
else: | ||||
return resources.is_resource( # pytype: disable=module-attr | ||||
pycompat.sysstr(package), encoding.strfromlocal(name) | ||||
) | ||||
r48668 | ||||
Mads Kiilerich
|
r53004 | def contents(package: bytes) -> "Iterator[bytes]": | ||
if hasattr(resources, 'files'): # Introduced in Python 3.9 | ||||
for path in resources.files(pycompat.sysstr(package)).iterdir(): | ||||
if path.is_file(): | ||||
yield encoding.strtolocal(path.name) | ||||
else: | ||||
# pytype: disable=module-attr | ||||
for r in resources.contents(pycompat.sysstr(package)): | ||||
# pytype: enable=module-attr | ||||
yield encoding.strtolocal(r) | ||||