aboutsummaryrefslogtreecommitdiffstats
path: root/devscripts
diff options
context:
space:
mode:
authorJesús <heckyel@hyperbola.info>2022-12-01 23:33:30 +0800
committerJesús <heckyel@hyperbola.info>2022-12-01 23:33:30 +0800
commitef1a420d6de7876b7b6732abc8ab78351c5a2bfc (patch)
tree9ba7d8409aa5baa696f5fb10db5d395c2f050276 /devscripts
parent16e8548f6a720a78679e417a20a300db2036bf6c (diff)
parentddf1e22d48530819d60220d0bdc36e20f5b8483b (diff)
downloadhypervideo-pre-ef1a420d6de7876b7b6732abc8ab78351c5a2bfc.tar.lz
hypervideo-pre-ef1a420d6de7876b7b6732abc8ab78351c5a2bfc.tar.xz
hypervideo-pre-ef1a420d6de7876b7b6732abc8ab78351c5a2bfc.zip
update from upstream 2022-12-01 UTC+8
Diffstat (limited to 'devscripts')
-rw-r--r--devscripts/__init__.py1
-rw-r--r--devscripts/lazy_load_template.py15
-rw-r--r--devscripts/make_lazy_extractors.py42
-rw-r--r--devscripts/make_readme.py27
-rw-r--r--devscripts/make_supportedsites.py12
-rw-r--r--devscripts/prepare_manpage.py41
-rwxr-xr-xdevscripts/run_tests.sh8
-rw-r--r--devscripts/set-variant.py36
-rw-r--r--devscripts/utils.py35
9 files changed, 148 insertions, 69 deletions
diff --git a/devscripts/__init__.py b/devscripts/__init__.py
new file mode 100644
index 000000000..750dbdca7
--- /dev/null
+++ b/devscripts/__init__.py
@@ -0,0 +1 @@
+# Empty file needed to make devscripts.utils properly importable from outside
diff --git a/devscripts/lazy_load_template.py b/devscripts/lazy_load_template.py
index cdafaf1ef..c8815e01b 100644
--- a/devscripts/lazy_load_template.py
+++ b/devscripts/lazy_load_template.py
@@ -9,14 +9,19 @@ from ..utils import (
write_string,
)
+# These bloat the lazy_extractors, so allow them to passthrough silently
+ALLOWED_CLASSMETHODS = {'extract_from_webpage', 'get_testcases', 'get_webpage_testcases'}
+_WARNED = False
+
class LazyLoadMetaClass(type):
def __getattr__(cls, name):
- # "_TESTS" bloat the lazy_extractors
- if '_real_class' not in cls.__dict__ and name != 'get_testcases':
- write_string(
- 'WARNING: Falling back to normal extractor since lazy extractor '
- f'{cls.__name__} does not have attribute {name}{bug_reports_message()}\n')
+ global _WARNED
+ if ('_real_class' not in cls.__dict__
+ and name not in ALLOWED_CLASSMETHODS and not _WARNED):
+ _WARNED = True
+ write_string('WARNING: Falling back to normal extractor since lazy extractor '
+ f'{cls.__name__} does not have attribute {name}{bug_reports_message()}\n')
return getattr(cls.real_class, name)
diff --git a/devscripts/make_lazy_extractors.py b/devscripts/make_lazy_extractors.py
index 785d66a6a..c502bdf89 100644
--- a/devscripts/make_lazy_extractors.py
+++ b/devscripts/make_lazy_extractors.py
@@ -2,34 +2,39 @@
# Allow direct execution
import os
+import shutil
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-import optparse
from inspect import getsource
+from devscripts.utils import get_filename_args, read_file, write_file
+
NO_ATTR = object()
-STATIC_CLASS_PROPERTIES = ['IE_NAME', 'IE_DESC', 'SEARCH_KEY', '_WORKING', '_NETRC_MACHINE', 'age_limit']
+STATIC_CLASS_PROPERTIES = [
+ 'IE_NAME', '_ENABLED', '_VALID_URL', # Used for URL matching
+ '_WORKING', 'IE_DESC', '_NETRC_MACHINE', 'SEARCH_KEY', # Used for --extractor-descriptions
+ 'age_limit', # Used for --age-limit (evaluated)
+ '_RETURN_TYPE', # Accessed in CLI only with instance (evaluated)
+]
CLASS_METHODS = [
- 'ie_key', 'working', 'description', 'suitable', '_match_valid_url', '_match_id', 'get_temp_id', 'is_suitable'
+ 'ie_key', 'suitable', '_match_valid_url', # Used for URL matching
+ 'working', 'get_temp_id', '_match_id', # Accessed just before instance creation
+ 'description', # Used for --extractor-descriptions
+ 'is_suitable', # Used for --age-limit
+ 'supports_login', 'is_single_video', # Accessed in CLI only with instance
]
IE_TEMPLATE = '''
class {name}({bases}):
_module = {module!r}
'''
-with open('devscripts/lazy_load_template.py', encoding='utf-8') as f:
- MODULE_TEMPLATE = f.read()
+MODULE_TEMPLATE = read_file('devscripts/lazy_load_template.py')
def main():
- parser = optparse.OptionParser(usage='%prog [OUTFILE.py]')
- args = parser.parse_args()[1] or ['yt_dlp/extractor/lazy_extractors.py']
- if len(args) != 1:
- parser.error('Expected only an output filename')
-
- lazy_extractors_filename = args[0]
+ lazy_extractors_filename = get_filename_args(default_outfile='yt_dlp/extractor/lazy_extractors.py')
if os.path.exists(lazy_extractors_filename):
os.remove(lazy_extractors_filename)
@@ -46,20 +51,20 @@ def main():
*build_ies(_ALL_CLASSES, (InfoExtractor, SearchInfoExtractor), DummyInfoExtractor),
))
- with open(lazy_extractors_filename, 'wt', encoding='utf-8') as f:
- f.write(f'{module_src}\n')
+ write_file(lazy_extractors_filename, f'{module_src}\n')
def get_all_ies():
PLUGINS_DIRNAME = 'ytdlp_plugins'
BLOCKED_DIRNAME = f'{PLUGINS_DIRNAME}_blocked'
if os.path.exists(PLUGINS_DIRNAME):
- os.rename(PLUGINS_DIRNAME, BLOCKED_DIRNAME)
+ # os.rename cannot be used, e.g. in Docker. See https://github.com/yt-dlp/yt-dlp/pull/4958
+ shutil.move(PLUGINS_DIRNAME, BLOCKED_DIRNAME)
try:
from yt_dlp.extractor.extractors import _ALL_CLASSES
finally:
if os.path.exists(BLOCKED_DIRNAME):
- os.rename(BLOCKED_DIRNAME, PLUGINS_DIRNAME)
+ shutil.move(BLOCKED_DIRNAME, PLUGINS_DIRNAME)
return _ALL_CLASSES
@@ -94,7 +99,7 @@ def sort_ies(ies, ignored_bases):
for c in classes[:]:
bases = set(c.__bases__) - {object, *ignored_bases}
restart = False
- for b in bases:
+ for b in sorted(bases, key=lambda x: x.__name__):
if b not in classes and b not in returned_classes:
assert b.__name__ != 'GenericIE', 'Cannot inherit from GenericIE'
classes.insert(0, b)
@@ -116,11 +121,6 @@ def build_lazy_ie(ie, name, attr_base):
}.get(base.__name__, base.__name__) for base in ie.__bases__)
s = IE_TEMPLATE.format(name=name, module=ie.__module__, bases=bases)
- valid_url = getattr(ie, '_VALID_URL', None)
- if not valid_url and hasattr(ie, '_make_valid_url'):
- valid_url = ie._make_valid_url()
- if valid_url:
- s += f' _VALID_URL = {valid_url!r}\n'
return s + '\n'.join(extra_ie_code(ie, attr_base))
diff --git a/devscripts/make_readme.py b/devscripts/make_readme.py
index f2e08d7c6..fad993a19 100644
--- a/devscripts/make_readme.py
+++ b/devscripts/make_readme.py
@@ -5,10 +5,17 @@ yt-dlp --help | make_readme.py
This must be run in a console of correct width
"""
+# Allow direct execution
+import os
+import sys
+
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
import functools
import re
-import sys
+
+from devscripts.utils import read_file, write_file
README_FILE = 'README.md'
@@ -38,6 +45,10 @@ switch_col_width = len(re.search(r'(?m)^\s{5,}', options).group())
delim = f'\n{" " * switch_col_width}'
PATCHES = (
+ ( # Standardize update message
+ r'(?m)^( -U, --update\s+).+(\n \s.+)*$',
+ r'\1Update this program to the latest version',
+ ),
( # Headings
r'(?m)^ (\w.+\n)( (?=\w))?',
r'## \1'
@@ -63,12 +74,10 @@ PATCHES = (
),
)
-with open(README_FILE, encoding='utf-8') as f:
- readme = f.read()
+readme = read_file(README_FILE)
-with open(README_FILE, 'w', encoding='utf-8') as f:
- f.write(''.join((
- take_section(readme, end=f'## {OPTIONS_START}'),
- functools.reduce(apply_patch, PATCHES, options),
- take_section(readme, f'# {OPTIONS_END}'),
- )))
+write_file(README_FILE, ''.join((
+ take_section(readme, end=f'## {OPTIONS_START}'),
+ functools.reduce(apply_patch, PATCHES, options),
+ take_section(readme, f'# {OPTIONS_END}'),
+)))
diff --git a/devscripts/make_supportedsites.py b/devscripts/make_supportedsites.py
index e46f7af56..01548ef97 100644
--- a/devscripts/make_supportedsites.py
+++ b/devscripts/make_supportedsites.py
@@ -7,21 +7,13 @@ import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-import optparse
-
+from devscripts.utils import get_filename_args, write_file
from yt_dlp.extractor import list_extractor_classes
def main():
- parser = optparse.OptionParser(usage='%prog OUTFILE.md')
- _, args = parser.parse_args()
- if len(args) != 1:
- parser.error('Expected an output filename')
-
out = '\n'.join(ie.description() for ie in list_extractor_classes() if ie.IE_DESC is not False)
-
- with open(args[0], 'w', encoding='utf-8') as outf:
- outf.write(f'# Supported sites\n{out}\n')
+ write_file(get_filename_args(), f'# Supported sites\n{out}\n')
if __name__ == '__main__':
diff --git a/devscripts/prepare_manpage.py b/devscripts/prepare_manpage.py
index d12ff4947..a393d33e1 100644
--- a/devscripts/prepare_manpage.py
+++ b/devscripts/prepare_manpage.py
@@ -1,9 +1,22 @@
#!/usr/bin/env python3
-import optparse
+# Allow direct execution
+import os
+import sys
+
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+
import os.path
import re
+from devscripts.utils import (
+ compose_functions,
+ get_filename_args,
+ read_file,
+ write_file,
+)
+
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
README_FILE = os.path.join(ROOT_DIR, 'README.md')
@@ -22,25 +35,6 @@ yt\-dlp \- A youtube-dl fork with additional features and patches
'''
-def main():
- parser = optparse.OptionParser(usage='%prog OUTFILE.md')
- _, args = parser.parse_args()
- if len(args) != 1:
- parser.error('Expected an output filename')
-
- outfile, = args
-
- with open(README_FILE, encoding='utf-8') as f:
- readme = f.read()
-
- readme = filter_excluded_sections(readme)
- readme = move_sections(readme)
- readme = filter_options(readme)
-
- with open(outfile, 'w', encoding='utf-8') as outf:
- outf.write(PREFIX + readme)
-
-
def filter_excluded_sections(readme):
EXCLUDED_SECTION_BEGIN_STRING = re.escape('<!-- MANPAGE: BEGIN EXCLUDED SECTION -->')
EXCLUDED_SECTION_END_STRING = re.escape('<!-- MANPAGE: END EXCLUDED SECTION -->')
@@ -92,5 +86,12 @@ def filter_options(readme):
return readme.replace(section, options, 1)
+TRANSFORM = compose_functions(filter_excluded_sections, move_sections, filter_options)
+
+
+def main():
+ write_file(get_filename_args(), PREFIX + TRANSFORM(read_file(README_FILE)))
+
+
if __name__ == '__main__':
main()
diff --git a/devscripts/run_tests.sh b/devscripts/run_tests.sh
index d496a092b..faa642e96 100755
--- a/devscripts/run_tests.sh
+++ b/devscripts/run_tests.sh
@@ -1,13 +1,13 @@
#!/usr/bin/env sh
-if [ -z $1 ]; then
+if [ -z "$1" ]; then
test_set='test'
-elif [ $1 = 'core' ]; then
+elif [ "$1" = 'core' ]; then
test_set="-m not download"
-elif [ $1 = 'download' ]; then
+elif [ "$1" = 'download' ]; then
test_set="-m download"
else
- echo 'Invalid test type "'$1'". Use "core" | "download"'
+ echo 'Invalid test type "'"$1"'". Use "core" | "download"'
exit 1
fi
diff --git a/devscripts/set-variant.py b/devscripts/set-variant.py
new file mode 100644
index 000000000..10341e744
--- /dev/null
+++ b/devscripts/set-variant.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+
+# Allow direct execution
+import os
+import sys
+
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+
+import argparse
+import functools
+import re
+
+from devscripts.utils import compose_functions, read_file, write_file
+
+VERSION_FILE = 'yt_dlp/version.py'
+
+
+def parse_options():
+ parser = argparse.ArgumentParser(description='Set the build variant of the package')
+ parser.add_argument('variant', help='Name of the variant')
+ parser.add_argument('-M', '--update-message', default=None, help='Message to show in -U')
+ return parser.parse_args()
+
+
+def property_setter(name, value):
+ return functools.partial(re.sub, rf'(?m)^{name}\s*=\s*.+$', f'{name} = {value!r}')
+
+
+opts = parse_options()
+transform = compose_functions(
+ property_setter('VARIANT', opts.variant),
+ property_setter('UPDATE_HINT', opts.update_message)
+)
+
+write_file(VERSION_FILE, transform(read_file(VERSION_FILE)))
diff --git a/devscripts/utils.py b/devscripts/utils.py
new file mode 100644
index 000000000..b91b8e65a
--- /dev/null
+++ b/devscripts/utils.py
@@ -0,0 +1,35 @@
+import argparse
+import functools
+
+
+def read_file(fname):
+ with open(fname, encoding='utf-8') as f:
+ return f.read()
+
+
+def write_file(fname, content, mode='w'):
+ with open(fname, mode, encoding='utf-8') as f:
+ return f.write(content)
+
+
+# Get the version without importing the package
+def read_version(fname='yt_dlp/version.py'):
+ exec(compile(read_file(fname), fname, 'exec'))
+ return locals()['__version__']
+
+
+def get_filename_args(has_infile=False, default_outfile=None):
+ parser = argparse.ArgumentParser()
+ if has_infile:
+ parser.add_argument('infile', help='Input file')
+ kwargs = {'nargs': '?', 'default': default_outfile} if default_outfile else {}
+ parser.add_argument('outfile', **kwargs, help='Output file')
+
+ opts = parser.parse_args()
+ if has_infile:
+ return opts.infile, opts.outfile
+ return opts.outfile
+
+
+def compose_functions(*functions):
+ return lambda x: functools.reduce(lambda y, f: f(y), functions, x)