Commit af2691f5 authored by Benjamin Winger's avatar Benjamin Winger

Fixed flake8 issues

parent d6d64089
......@@ -32,7 +32,10 @@ if __name__ == "__main__":
parser.add_argument(
"-m",
metavar="MOD",
help="Mod for setting local use flag. If not set, enables/disables global use flags.",
help=(
"Mod for setting local use flag. "
"If not set, enables/disables global use flags."
),
)
parser.add_argument("--debug", help="Enables debug traces", action="store_true")
args = parser.parse_args()
......
......@@ -3,7 +3,6 @@
# Copyright 2019 Portmod Authors
# Distributed under the terms of the GNU General Public License v3
import os
import sys
import argparse
from os import path as osp
......
......@@ -7,6 +7,7 @@ import shutil
import re
from operator import itemgetter
# Returns config file as a list of strings (one string per line)
def read_config():
config = {"data": [], "content": [], "fallback-override": []}
......@@ -50,8 +51,11 @@ def add_config(config, prefix, name, index=-1):
print('Added "{}" to config'.format("{}={}".format(prefix, name)))
# Checks if config contains a line matching the parameters. Supports globbing in the prefix and name
def check_config(config, prefix, name):
"""
Checks if config contains a line matching the parameters.
Supports globbing in the prefix and name
"""
for line in config.get(prefix, []):
if fnmatch.fnmatch(line, name) or fnmatch.fnmatch(line, '"{}"'.format(name)):
return config[prefix].index(line)
......@@ -65,8 +69,10 @@ def check_config_subdirs(config, prefix, name):
return index
# Returns index-value pairs for lines in config that match the given prefix and name
def find_config(config, prefix, name):
"""
Returns index-value pairs for lines in config that match the given prefix and name
"""
lines = []
for (index, line) in enumerate(config[prefix]):
if fnmatch.fnmatch(line, name) or fnmatch.fnmatch(line, '"{}"'.format(name)):
......@@ -87,8 +93,11 @@ def Diff(li1, li2):
return li_dif
# Removes lines from the config matching the parameters. Supports globbing in the prefix and name
def remove_config(config, prefix, name):
"""
Removes lines from the config matching the parameters.
Supports globbing in the prefix and name
"""
to_remove = find_config(config, prefix, name)
# delete in reverse order to preserve indexes
for (index, line) in sorted(to_remove, key=itemgetter(0), reverse=True):
......
......@@ -8,7 +8,6 @@ import string
import shutil
import subprocess
import re
from operator import itemgetter
from portmod.repo.download import download, get_hash, get_filename
from portmod.repos import Repo, REPOS, REPOS_FILE
from portmod.globals import TMP_DIR, PORTMOD_LOCAL_DIR
......@@ -18,7 +17,6 @@ from portmod.colour import colour
from portmod.repo.manifest import create_manifest, get_manifest, Manifest, FileType
from portmod.masters import get_masters
from colorama import Fore
from .log import err
USER_REPO = Repo(
"user-repo",
......@@ -30,12 +28,14 @@ USER_REPO = Repo(
50,
)
# Detect masters using tes3cmd and determine which atom they correspond to
def get_esp_deps(file, datadirs):
masters = get_masters(file)
use = set()
# Check if masters correspond to Tribunal.esm or Bloodmoon.esm, as they are pulled in with global use flags instead of a mod
# Check if masters correspond to Tribunal.esm or Bloodmoon.esm, as they are pulled
# in with global use flags instead of a mod
if "Tribunal.esm" in masters:
use.add("tribunal")
masters.remove("Tribunal.esm")
......@@ -130,7 +130,8 @@ def import_mod(mod):
bsa_string = ""
d_esps = []
# Get dependencies for the ESP. List dependencies common to all ESPs as deps for the mod, others
# Get dependencies for the ESP.
# List dependencies common to all ESPs as deps for the mod
for esp in esps:
(dep_atom, dep_use) = get_esp_deps(
os.path.join(outdir, directory, esp),
......@@ -279,26 +280,28 @@ def is_data_dir(directory):
for file in directory[2]:
# Match .bsa, .esp and .esm files
if re.match(
"^.*\.([eE][sS][pP]|[bB][sS][aA]|[eE][sS][mM]|[oO][mM][wW][aA][dD][dD][oO][nN])$",
r"^.*\.([eE][sS][pP]|"
r"[bB][sS][aA]|[eE][sS][mM]|"
r"[oO][mM][wW][aA][dD][dD][oO][nN])$",
file,
):
return True
if re.match("^.*\.([dD][dD][sS])$", file) and not re.match(
".*([Tt]extures|[iI]cons|[bB]ook[Aa]rt|[dD]istant[lL]and).*", path
if re.match(r"^.*\.([dD][dD][sS])$", file) and not re.match(
r".*([Tt]extures|[iI]cons|[bB]ook[Aa]rt|[dD]istant[lL]and).*", path
):
# Looks like its a textures directory that does not have a standard parent directory. We may not pick it up otherwise.
# Looks like its a textures directory that does not have a standard parent
# directory. We may not pick it up otherwise.
print(
"Detected textures in directory that is not a subdir of a common directory. This may be an optional set of textures, or it may be something else. Double-check before selecting this option: {}".format(
path
)
"Detected textures in directory that is not a subdir of a common "
"directory. This may be an optional set of textures, or it may be "
"something else. Double-check this: {}".format(path)
)
return True
return False
# Fixes structure of install directory and returns a list of location of any data directories found
def find_data_dirs(path):
# Search for possible data directories and prompt the user to select the ones they want to include if multiple are found.
# Search for possible data directories
return [os.path.relpath(x[0], path) for x in os.walk(path) if is_data_dir(x)]
......
......@@ -5,6 +5,7 @@ import shutil
import subprocess
import shlex
# Detects masters using tes3cmd
def get_masters(file):
omwcmd = shutil.which("omwcmd")
......
# Copyright 2019 Portmod Authors
# Distributed under the terms of the GNU General Public License v3
import sys, re
import sys
import re
from distutils.util import strtobool
from portmod.colour import bright, lgreen, lred
......@@ -27,10 +28,10 @@ def parse_num_list(string):
m = re.match(r"(\d+)(?:-(\d+))?$", string)
if not m:
raise ArgumentTypeError(
"'"
+ string
+ "' is not a range of number. Expected forms like '0-5' or '2'."
raise TypeError(
"'{}' is not a range of number. Expected forms like '0-5' or '2'.".format(
string
)
)
start = m.group(1)
end = m.group(2) or start
......@@ -38,7 +39,7 @@ def parse_num_list(string):
def prompt_num_multi(question, max_val):
sys.stdout.write("%s: " % question)
print("{}: ".format(question))
while True:
try:
result = [y for x in input().split(",") for y in parse_num_list(x)]
......@@ -51,8 +52,9 @@ def prompt_num_multi(question, max_val):
else:
return result
except ValueError:
sys.stdout.write(
"Please respond using a-b to indicate a range and a,b to indicate individual numbers: "
print(
"Please respond using a-b to indicate a range and a,b "
"to indicate individual numbers: "
)
......
......@@ -11,7 +11,7 @@ from distutils.dir_util import copy_tree
from portmod.globals import MOD_DIR
from portmod.repo.atom import Atom, InvalidAtom
from portmod.config import read_config, write_config, add_config, remove_config
from portmod.repo.use import get_use, satisfies_use, use_reduce, check_required_use
from portmod.repo.use import get_use, use_reduce, check_required_use
from portmod.repo.manifest import get_manifest
from portmod.repo.util import get_hash
from portmod.repo.metadata import get_global_use, get_mod_metadata, license_exists
......@@ -23,8 +23,8 @@ from ..pybuild_interface import Pybuild
import inspect
# This is a terrible hack function, but it does the job. This should only be used within pybuild files
# macropy may provide a more robust way of implementing this
# This is a terrible hack function, but it does the job. This should only be used
# within pybuild files. macropy may provide a more robust way of implementing this
def ModInfo():
frame = inspect.currentframe()
try:
......@@ -158,19 +158,21 @@ class Pybuild1(Pybuild):
)
else:
raise TypeError(
"TEXTURE_SIZES must be a string containing a space separated list of texture sizes"
"TEXTURE_SIZES must be a string containing a space separated list of "
"texture sizes"
)
all_sources = self.get_sources([], [], matchall=True)
for install in self.INSTALL_DIRS:
if type(install) is type(InstallDir("")):
if type(install) is InstallDir:
if len(all_sources) > 0 and install.SOURCE is None:
if len(all_sources) == 1:
install.SOURCE = all_sources[0].name
else:
raise Exception(
"InstallDir does not declare a source name but source cannot be set automatically"
"InstallDir does not declare a source name but source "
"cannot be set automatically"
)
else:
raise TypeError(
......@@ -194,7 +196,8 @@ class Pybuild1(Pybuild):
matchnone=False,
matchall=False,
) -> List[Source]:
is_valid = lambda x: x in self.IUSE_EFFECTIVE
def is_valid(x):
return x in self.IUSE_EFFECTIVE
def group(sourcelist):
result = []
......@@ -291,7 +294,9 @@ class Pybuild1(Pybuild):
dest = os.path.join(self.D, install_dir.DESTPATH)
for file in install_dir.ARCHIVES + install_dir.PLUGINS:
# Remove files that aren't going to be used. We would like the user to enable them with use flags rather than manually
# Remove files that aren't going to be used.
# We would like the user to enable them with use flags rather
# than manually
if not check_required_use(
file.REQUIRED_USE, self.get_use()[0], self.valid_use
):
......@@ -300,9 +305,8 @@ class Pybuild1(Pybuild):
copy_tree(source, dest)
else:
print(
"Skipping directory {} due to unsatisfied use requirements {}".format(
install_dir.PATH, install_dir.REQUIRED_USE
)
"Skipping directory {} due to unsatisfied use "
"requirements {}".format(install_dir.PATH, install_dir.REQUIRED_USE)
)
def mod_postinst(self):
......@@ -333,13 +337,13 @@ class Pybuild1(Pybuild):
all_sources = self.get_sources([], [], matchall=True)
for install in self.INSTALL_DIRS:
if type(install) != type(InstallDir("")):
if type(install) is not InstallDir:
errors.append(
'InstallDir "{}" must have type InstallDir'.format(install.PATH)
)
continue
for file in install.PLUGINS + install.ARCHIVES:
if type(file) != type(File("")):
if type(file) is not File:
errors.append('File "{}" must have type File'.format(file))
continue
......@@ -374,9 +378,8 @@ class Pybuild1(Pybuild):
metadata is None or metadata.get("use", {}).get(use, None) is None
):
errors.append(
'Use flag "{}" must be either a global use flag or declared in metadata.yaml'.format(
use
)
'Use flag "{}" must be either a global use flag '
"or declared in metadata.yaml".format(use)
)
if self.NAME is None or "FILLME" in self.NAME or len(self.NAME) == 0:
......@@ -392,15 +395,16 @@ class Pybuild1(Pybuild):
if self.LICENSE is None:
errors.append(
"You must specify a LICENSE for the mod (i.e., the License the mod uses)"
"You must specify a LICENSE for the mod "
"(i.e., the License the mod uses)"
)
else:
for license in self.LICENSE:
if not license_exists(self.__REPO_PATH, license):
errors.append(
"LICENSE {} does not exit! Please make sure that it named correctly, or if it is a new License that it is added to the licenses directory of the repository".format(
license
)
"LICENSE {} does not exit! Please make sure that it named "
"correctly, or if it is a new License that it is added to "
"the licenses directory of the repository".format(license)
)
if len(errors) > 0:
......
......@@ -7,7 +7,7 @@ import subprocess
def tr_patcher(filename):
process = subprocess.Popen(
subprocess.Popen(
shlex.split('{} "{}"'.format(shutil.which("tr-patcher"), filename)),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
......
from operator import attrgetter
from colorama import Fore, Style
from portmod.colour import green, bright
from portmod.repo.manifest import get_total_download_size
from portmod.repo.loader import load_all, load_all_installed, load_installed_mod
......@@ -9,18 +6,16 @@ from portmod.repo import Atom
def query(field, value):
return set([mod for mod in load_all() if value in getattr(mod, field)])
return {mod for mod in load_all() if value in getattr(mod, field)}
def query_insensitive(field, value):
return set(
[mod for mod in load_all() if value.lower() in getattr(mod, field).lower()]
)
return {mod for mod in load_all() if value.lower() in getattr(mod, field).lower()}
def query_installed(field, value):
installed = [mod for group in load_all_installed().values() for mod in group]
return set([mod for mod in installed if value in getattr(mod, field)])
return {mod for mod in installed if value in getattr(mod, field)}
def display_search_results(mods):
......
# Copyright 2019 Portmod Authors
# Distributed under the terms of the GNU General Public License v3
from portmod.repo.atom import Atom
from portmod.repo.atom import Atom # noqa # pylint: disable=unused-import
......@@ -253,13 +253,15 @@ def sort_config():
print()
print("\n".join(extraneous))
warn(
"The above Data Directories were in openmw.cfg but were not installed by portmod. They have been appended to the end of the data directory list."
"The above Data Directories were in openmw.cfg but were not installed by "
"portmod. They have been appended to the end of the data directory list."
)
config["data"] = new_dirs + extraneous
# Sort 'content' entries in config
# Create graph of content files that are installed, with masters of a file being the parent of the node in the graph
# Create graph of content files that are installed, with masters of a file being
# the parent of the node in the graph
# Any other content files found in the config should be warned about and removed.
graph = {}
priorities = {}
......@@ -309,7 +311,8 @@ def sort_config():
print()
print("\n".join(extraneous))
warn(
"The above Content files were in openmw.cfg but were not installed by portmod. They have been appended to the end of the load list."
"The above Content files were in openmw.cfg but were not installed by "
"portmod. They have been appended to the end of the load list."
)
config["content"] = new_esps + extraneous
......@@ -336,7 +339,8 @@ def sort_config():
print()
print("\n".join(extraneous))
warn(
"The above Fallback Archives were in openmw.cfg but were not installed by portmod. They have been appended to the end of the load list."
"The above Fallback Archives were in openmw.cfg but were not installed "
"by portmod. They have been appended to the end of the load list."
)
config["fallback-archive"] = new_bsas + extraneous
......
......@@ -31,9 +31,9 @@ def add_flag(file, atom, flag):
found = False
for (index, line) in enumerate(flagfile):
l = line.split()
if l[0] == atom:
if flag not in l:
tokens = line.split()
if tokens[0] == atom:
if flag not in tokens:
print("Adding flag {} to {} in {}".format(flag, atom, file))
file[index] = "{} {}".format(line, flag)
found = True
......@@ -48,13 +48,13 @@ def remove_flag(file, atom, flag):
flagfile = __read_flags(file)
for (index, line) in enumerate(flagfile):
l = line.split()
if atom_sat(Atom(l[0]), atom) and flag in l:
tokens = line.split()
if atom_sat(Atom(tokens[0]), atom) and flag in tokens:
print("Removing flag {} from {} in {}".format(flag, atom, file))
l = list(filter(lambda a: a != flag, l))
tokens = list(filter(lambda a: a != flag, tokens))
if len(l) > 1:
flagfile[index] = " ".join(l)
if len(tokens) > 1:
flagfile[index] = " ".join(tokens)
else:
del flagfile[index]
......
......@@ -7,8 +7,9 @@ from portmod.repo.flags import add_flag, remove_flag, get_flags
__LOCAL_KEYWORDS = None
# Adds keyword for the given atom. Does not modify any existing keywords.
def add_keyword(atom, keyword):
"""Adds keyword for the given atom. Does not modify any existing keywords."""
keyword_file = os.path.join(PORTMOD_CONFIG_DIR, "mod.accept_keywords")
add_flag(keyword_file, atom, keyword)
......@@ -30,12 +31,14 @@ def accepts_testing(arch, keywords):
def accepts(accept_keywords, keywords):
for keyword in accept_keywords:
if keyword.startswith("~"):
# Accepts testing on this architecture. Valid if keywords contains either testing or stable for this keyword
# Accepts testing on this architecture. Valid if keywords contains either
# testing or stable for this keyword
if keyword in keywords or keyword[1:] in keywords:
return True
pass
elif keyword == "*":
# Accepts stable on all architectures. Valid if keywords contains a stable keyword for any keyword
# Accepts stable on all architectures. Valid if keywords contains a stable
# keyword for any keyword
if any(
[
not keyword.startswith("~") and not keyword.startswith("*")
......@@ -44,7 +47,8 @@ def accepts(accept_keywords, keywords):
):
return True
elif keyword == "~*":
# Accepts testing on all architectures. Valid if keywords contains either testing or stable for any keyword
# Accepts testing on all architectures. Valid if keywords contains either
# testing or stable for any keyword
if any([not keyword.startswith("*") for keyword in keywords]):
return True
elif keyword == "**":
......
......@@ -11,9 +11,11 @@ from portmod.globals import INSTALLED_DB
from portmod.repos import REPOS
from portmod.repo.metadata import get_categories
import portmod.globals
from .atom import Atom
# We store a cache of mods so that they are only loaded once when doing dependency resolution.
# We store a cache of mods so that they are only loaded once
# when doing dependency resolution.
# Stores key-value pairs of the form (filename, Mod Object)
__mods = {}
......@@ -56,6 +58,7 @@ def __load_installed_mod(path):
if os.path.exists(path):
files = glob.glob(os.path.join(path, "*.pybuild"))
if len(files) > 1:
atom = Atom(files[0].rstrip(".pybuild"))
raise Exception('Multiple versions of mod "{}" installed!'.format(atom))
elif len(files) == 0:
return None
......@@ -65,7 +68,7 @@ def __load_installed_mod(path):
return __mods[file]
else:
mod = load_file(file)
if mod == None:
if mod is None:
continue
mod.INSTALLED = True
......@@ -105,9 +108,13 @@ def load_installed_mod(atom):
def load_mod(atom):
mods = (
[]
) # We will return every single mod matching this name. There may be multiple versions in different repos, as well versions with different version or release numbers
"""
Loads all mods matching the given atom
There may be multiple versions in different repos,
as well versions with different version or release numbers
"""
mods = []
path = None
for repo in REPOS:
if not os.path.exists(repo.location):
......
......@@ -10,7 +10,6 @@ from portmod.repo.download import (
clobber_spaces,
)
from portmod.log import err
from portmod.repo.use import satisfies_use
class ManifestFile:
......@@ -68,12 +67,8 @@ class Manifest:
)
def add_to_manifest(file):
with open(manifest_path(pybuild_file), "w") as manifest:
manifest.writelines(lines)
# Atomatically download mod DIST files (if not already in cache) and create a manifest file
# Atomatically download mod DIST files (if not already in cache)
# and create a manifest file
def create_manifest(mod):
clobber_spaces()
pybuild_file = mod.FILE
......
......@@ -64,9 +64,8 @@ def add_use(flag, atom=None, disable=False):
(newest, req) = select_mod(loader.load_mod(atom))
if flag not in newest.IUSE:
print(
"Warning: {} is not a valid use flag for {}, the default selected version of mod {}".format(
flag, newest.ATOM, atom
)
"Warning: {} is not a valid use flag for {}, "
"the default selected version of mod {}".format(flag, newest.ATOM, atom)
)
if atom:
......@@ -280,12 +279,12 @@ def use_reduce(
)
if level > 0:
level -= 1
l = stack.pop()
ll = stack.pop()
is_single = (
len(l) == 1
or (opconvert and l and l[0] == "||")
or (not opconvert and len(l) == 2 and l[0] == "||")
len(ll) == 1
or (opconvert and ll and ll[0] == "||")
or (not opconvert and len(ll) == 2 and ll[0] == "||")
)
ignore = False
......@@ -297,17 +296,17 @@ def use_reduce(
# Merge the current list if needed.
if is_active(stack[level][-1]):
stack[level].pop()
stack[level].extend(l)
stack[level].extend(ll)
else:
stack[level].pop()
else:
stack[level].extend(l)
stack[level].extend(ll)
continue
if stack[level] and isinstance(stack[level][-1], str):
if stack[level][-1] == "||" and not l:
if stack[level][-1] == "||" and not ll:
# Optimize: || ( ) -> .
l.append((token_class or str)("empty-any-of"))
ll.append((token_class or str)("empty-any-of"))
stack[level].pop()
elif stack[level][-1][-1] == "?":
# The last token before the '(' that matches the current ')'
......@@ -345,38 +344,38 @@ def use_reduce(
"""
if is_single:
# Either [A], [[...]] or [|| [...]]
if l[0] == "||" and ends_in_any_of_dep(level - 1):
if ll[0] == "||" and ends_in_any_of_dep(level - 1):
if opconvert:
stack[level].extend(l[1:])
stack[level].extend(ll[1:])
else:
stack[level].extend(l[1])
elif len(l) == 1 and isinstance(l[0], list):
stack[level].extend(ll[1])
elif len(ll) == 1 and isinstance(ll[0], list):
# l = [[...]]
last = last_any_of_operator_level(level - 1)
if last == -1:
if (
opconvert
and isinstance(l[0], list)
and l[0]
and l[0][0] == "||"
and isinstance(ll[0], list)
and ll[0]
and ll[0][0] == "||"
):
stack[level].append(l[0])
stack[level].append(ll[0])
else:
stack[level].extend(l[0])
stack[level].extend(ll[0])
else:
if opconvert and l[0] and l[0][0] == "||":
stack[level].extend(l[0][1:])
if opconvert and ll[0] and ll[0][0] == "||":
stack[level].extend(ll[0][1:])
else:
stack[level].append(l[0])
stack[level].append(ll[0])
else:
stack[level].extend(l)
stack[level].extend(ll)
else:
if opconvert and stack[level] and stack[level][-1] == "||":
stack[level][-1] = ["||"] + l
stack[level][-1] = ["||"] + ll
else:
stack[level].append(l)
stack[level].append(ll)
if l and not ignore:
if ll and not ignore:
# The current list is not empty and we don't want to ignore it
# because of an inactive use conditional.
if not ends_in_any_of_dep(level - 1) and not ends_in_any_of_dep(
......@@ -384,7 +383,7 @@ def use_reduce(
):
# Optimize: ( ( ... ) ) -> ( ... ).
# Make sure there is no '||' hanging around.
stack[level].extend(l)
stack[level].extend(ll)
elif not stack[level]:
# An '||' in the level above forces us to keep to brackets.
special_append()
......@@ -395,13 +394,13 @@ def use_reduce(
elif ends_in_any_of_dep(level) and ends_in_any_of_dep(level - 1):
# Optimize: || ( A || ( B C ) ) -> || ( A B C )
stack[level].pop()
stack[level].extend(l)
stack[level].extend(ll)
else:
if opconvert and ends_in_any_of_dep(level):
# In opconvert mode, we have to move the operator from the
# level above into the current list.
stack[level].pop()
stack[level].append(["||"] + l)
stack[level].append(["||"] + ll)
else:
special_append()
......@@ -533,12 +532,12 @@ def check_required_use(
raise Exception("malformed syntax: '%s'" % required_use)
if level > 0:
level -= 1
l = stack.pop()
ll = stack.pop()
op = None
if stack[level]:
if stack[level][-1] in valid_operators:
op = stack[level].pop()
satisfied = is_satisfied(op, l)
satisfied = is_satisfied(op, ll)
stack[level].append(satisfied)
elif (
......@@ -547,14 +546,14 @@ def check_required_use(
):
op = stack[level].pop()
if is_active(op[:-1]):
satisfied = is_satisfied(op, l)
satisfied = is_satisfied(op, ll)
stack[level].append(satisfied)
else:
continue
if op is None:
satisfied = False not in l
if l:
satisfied = False not in ll
if ll:
stack[level].append(satisfied)
else:
raise Exception("malformed syntax: '%s'" % required_use)
......
......@@ -23,8 +23,8 @@ class LicenseDep:
def select_mod(modlist):
# Since mod Licenses rarely change between versions, choose a mod version based on keywords
# and accept it if the license is accepted
# Since mod Licenses rarely change between versions, choose a mod version based
# on keywords and accept it if the license is accepted
(mod, usedep) = select_mod_by_use(modlist)
if not is_license_accepted(mod):
......@@ -44,7 +44,8 @@ def select_mod_by_use(modlist):
)
if len(filtered) == 0:
# No mods were accepted. Prompt user to add an exception for the best version in this modlist
# No mods were accepted. Prompt user to add an exception for the best version
# in this modlist
unstable = list(
filter(lambda mod: accepts_testing(ARCH, mod.KEYWORDS), modlist)
)
......@@ -52,7 +53,8 @@ def select_mod_by_use(modlist):
newest_unstable = get_newest_mod(unstable)
return (newest_unstable, "~" +