mirror of
https://github.com/frida/frida-python.git
synced 2026-11-07 09:35:57 -10:00
Remove PEP503 parser and egg downloader from setup.py
This commit is contained in:
1 parent
85e7cb9b94
commit
2c805431bd
2 files changed
+5
-462
No files matched your search
@@ -1,265 +1,25 @@
|
||||
import codecs
|
||||
import hashlib
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import zipfile
|
||||
from collections import namedtuple
|
||||
from functools import partial
|
||||
from html.parser import HTMLParser
|
||||
from io import BytesIO
|
||||
from urllib.parse import urljoin, urlparse, urlunparse
|
||||
from urllib.request import urlopen
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools.command.build_ext import build_ext
|
||||
from setuptools.extension import Extension
|
||||
|
||||
DEFAULT_INDEX_URL = "https://pypi.org/simple/"
|
||||
|
||||
python_version = sys.version_info[0:2]
|
||||
|
||||
package_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
pkg_info = os.path.join(package_dir, "PKG-INFO")
|
||||
in_source_package = os.path.isfile(pkg_info)
|
||||
if in_source_package:
|
||||
with codecs.open(pkg_info, "r", "utf-8") as f:
|
||||
version_line = [line.rstrip("\r") for line in f.read().split("\n") if line.startswith("Version: ")][0]
|
||||
frida_version = version_line[9:]
|
||||
long_description = None
|
||||
else:
|
||||
frida_version = os.environ.get("FRIDA_VERSION", None)
|
||||
long_description = codecs.open(os.path.join(package_dir, "README.md"), "r", "utf-8").read()
|
||||
frida_extension = os.environ.get("FRIDA_EXTENSION", None)
|
||||
|
||||
index_url_pip_configs = ("global.index-url", "global.extra-index-url")
|
||||
|
||||
Tag = namedtuple("Tag", ["tagname", "attrs"])
|
||||
ParsedUrlInfo = namedtuple("ParsedUrlInfo", ["url", "filename", "major", "minor", "micro"])
|
||||
frida_version = os.environ.get("FRIDA_VERSION", None)
|
||||
long_description = codecs.open(os.path.join(package_dir, "README.md"), "r", "utf-8").read()
|
||||
frida_extension = os.environ.get("FRIDA_EXTENSION", None)
|
||||
|
||||
|
||||
class FridaPrebuiltExt(build_ext):
|
||||
def build_extension(self, ext):
|
||||
target = self.get_ext_fullpath(ext.name)
|
||||
target_extension = os.path.splitext(target)[1]
|
||||
target_dir = os.path.dirname(target)
|
||||
try:
|
||||
os.makedirs(target_dir)
|
||||
except:
|
||||
pass
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
|
||||
if in_source_package:
|
||||
system = platform.system()
|
||||
arch = struct.calcsize("P") * 8
|
||||
if system == "Windows":
|
||||
os_version = "win-amd64" if arch == 64 else "win32"
|
||||
elif system == "Darwin":
|
||||
if platform.machine() == "x86_64":
|
||||
os_version = "macosx-10.9-x86_64"
|
||||
else:
|
||||
os_version = "macosx-11.0-arm64"
|
||||
elif system == "Linux":
|
||||
os_name = (
|
||||
"android"
|
||||
if subprocess.check_output(["uname", "-o"]).decode("utf-8").rstrip() == "Android"
|
||||
else "linux"
|
||||
)
|
||||
machine = platform.machine()
|
||||
if machine == "" or "86" in machine:
|
||||
arch_name = "x86_64" if arch == 64 else "i686"
|
||||
elif os_name == "android" and machine.startswith("armv"):
|
||||
arch_name = "armv7l"
|
||||
else:
|
||||
arch_name = machine
|
||||
os_version = f"{os_name}-{arch_name}"
|
||||
elif system == "FreeBSD":
|
||||
os_version = "freebsd-" + platform.machine()
|
||||
else:
|
||||
raise NotImplementedError("unsupported OS")
|
||||
|
||||
egg_path = os.path.expanduser(
|
||||
f"~{os.sep}frida-{frida_version}-py{python_version[0]}.{python_version[1]}-{os_version}.egg"
|
||||
)
|
||||
print("looking for prebuilt extension in home directory, i.e.", egg_path)
|
||||
|
||||
try:
|
||||
with open(egg_path, "rb") as cache:
|
||||
egg_data = cache.read()
|
||||
except:
|
||||
egg_data = None
|
||||
|
||||
if egg_data is None:
|
||||
print("prebuilt extension not found in home directory, will try downloading it")
|
||||
|
||||
print("querying pypi for available prebuilds")
|
||||
# index_url is a url compatible with PEP 503
|
||||
index_url = get_index_url().strip()
|
||||
index_url = normalize_url(index_url)
|
||||
frida_url = urljoin(index_url, "frida/") # slash is necessary here
|
||||
timeout = 20
|
||||
errmsg = "unable to download it within {} seconds; " f"please download it manually to {egg_path}"
|
||||
|
||||
print("downloading package list from", frida_url)
|
||||
try:
|
||||
links_html = urlopen(frida_url, timeout=timeout).read().decode("utf-8")
|
||||
except Exception:
|
||||
print(errmsg.format(timeout))
|
||||
raise
|
||||
|
||||
parser = PEP503PageParser("frida", frida_version, os_version)
|
||||
parser.feed(links_html)
|
||||
|
||||
if len(parser.urls) == 0:
|
||||
raise NotImplementedError(
|
||||
"could not find prebuilt Frida extension; " "prebuilds only provided for Python 3.4+"
|
||||
)
|
||||
|
||||
url = parser.urls[0]
|
||||
egg_url = urljoin(frida_url, url.url)
|
||||
|
||||
try:
|
||||
print("downloading prebuilt extension from", egg_url)
|
||||
timeout = 120 # We'll assume the user has at least 200 kB/s transfer speed.
|
||||
egg_data = urlopen(egg_url, timeout=timeout).read()
|
||||
except Exception:
|
||||
print(errmsg.format(timeout))
|
||||
raise
|
||||
else:
|
||||
egg_url = None
|
||||
|
||||
egg_file = BytesIO(egg_data)
|
||||
|
||||
if egg_url is not None:
|
||||
print("checking hash")
|
||||
check_pep503_hash(egg_file, egg_url)
|
||||
|
||||
print("extracting prebuilt extension")
|
||||
egg_zip = zipfile.ZipFile(egg_file)
|
||||
extension_member = [info for info in egg_zip.infolist() if info.filename.endswith(target_extension)][0]
|
||||
extension_data = egg_zip.read(extension_member)
|
||||
if system == "Windows":
|
||||
trailer = b"\x00" if python_version[1] >= 10 else b"\x00\x00"
|
||||
extension_data = re.sub(
|
||||
b"python[3-9][0-9][0-9]\\.dll\x00",
|
||||
"python{}{}.dll".format(*python_version).encode("utf-8") + trailer,
|
||||
extension_data,
|
||||
)
|
||||
with open(target, "wb") as f:
|
||||
f.write(extension_data)
|
||||
else:
|
||||
shutil.copyfile(frida_extension, target)
|
||||
|
||||
|
||||
def get_index_url():
|
||||
"""get `index-url` from environment or pip
|
||||
Use FRIDA_INDEX_URL environment variable to customize index-url compatible
|
||||
with PEP 503.
|
||||
"""
|
||||
index_url = os.environ.get("FRIDA_INDEX_URL", None)
|
||||
if index_url is not None:
|
||||
return index_url
|
||||
|
||||
for config_name in index_url_pip_configs:
|
||||
try:
|
||||
index_url = get_index_url_from_pip(config_name)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
pass
|
||||
else:
|
||||
return index_url
|
||||
|
||||
print(f"using default index URL: {DEFAULT_INDEX_URL}")
|
||||
return DEFAULT_INDEX_URL
|
||||
|
||||
|
||||
def get_index_url_from_pip(config_name):
|
||||
assert config_name in index_url_pip_configs
|
||||
|
||||
return subprocess.check_output(
|
||||
[sys.executable, "-m", "pip", "config", "get", config_name], stderr=subprocess.PIPE
|
||||
).decode("utf-8")
|
||||
|
||||
|
||||
def normalize_url(url):
|
||||
parse_result = urlparse(url)
|
||||
path = parse_result.path
|
||||
if not path.endswith("/"):
|
||||
path += "/"
|
||||
return urlunparse(
|
||||
(
|
||||
parse_result.scheme,
|
||||
parse_result.netloc,
|
||||
path,
|
||||
parse_result.params,
|
||||
parse_result.query,
|
||||
parse_result.fragment,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class PEP503PageParser(HTMLParser):
|
||||
def __init__(self, name, version, os_version):
|
||||
HTMLParser.__init__(self)
|
||||
filename_pattern = (r"^{}\-{}\-py(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<micro>\d+))?-{}.egg$").format(
|
||||
*map(re.escape, [name, version, os_version])
|
||||
)
|
||||
self._filename_pattern = re.compile(filename_pattern)
|
||||
|
||||
def reset(self):
|
||||
HTMLParser.reset(self)
|
||||
self._path = []
|
||||
self.urls = []
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
self._path.append(Tag(tag, dict(attrs)))
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
if tag == "a":
|
||||
while True:
|
||||
if self._path.pop().tagname == tag:
|
||||
break
|
||||
else:
|
||||
if len(self._path) > 0 and self._path[-1].tagname == tag:
|
||||
self._path.pop()
|
||||
|
||||
def handle_data(self, data):
|
||||
if not (len(self._path) > 0 and self._path[-1].tagname == "a" and self._path[-1].attrs.get("href")):
|
||||
return
|
||||
|
||||
match = self._filename_pattern.match(data)
|
||||
if match is not None:
|
||||
self.urls.append(
|
||||
ParsedUrlInfo(
|
||||
self._path[-1].attrs["href"],
|
||||
data,
|
||||
*map(lambda g: int(g) if g else None, map(match.group, ["major", "minor", "micro"])),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def check_pep503_hash(bytes_io, url):
|
||||
parse_result = urlparse(url)
|
||||
fragment = parse_result.fragment
|
||||
if fragment == "":
|
||||
return
|
||||
|
||||
hashname, hashvalue = fragment.split("=")
|
||||
if hashname not in {"md5", "sha1", "sha224", "sha256", "sha348", "sha512"}:
|
||||
raise ValueError(f"Unsupported hash algorithm: {hashname}, hashvalue={hashvalue}")
|
||||
|
||||
h = hashlib.new(hashname)
|
||||
for block in iter(partial(bytes_io.read, 4096), b""): # iterate until EOF
|
||||
h.update(block)
|
||||
digest = h.hexdigest()
|
||||
bytes_io.seek(0) # reset offset
|
||||
|
||||
if digest == hashvalue:
|
||||
return
|
||||
else:
|
||||
raise ValueError(f"`{hashname}` hash checking failed! Expected: {hashvalue}, but got: {digest}")
|
||||
shutil.copyfile(frida_extension, target)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in new issue
Block a user