From: Fabian Groffen Date: Thu, 4 Feb 2010 19:09:17 +0000 (-0000) Subject: Merged from trunk -r15303:15318 X-Git-Url: http://git.tremily.us/gitweb.cgi?a=commitdiff_plain;h=0f365e2d102af2de32b7c77e490f741d1d37f1a9;p=portage.git Merged from trunk -r15303:15318 | 15304 | Bug #302937 - Handle declare -r without assignment. | | zmedico | | | 15305 | Validate categories. | | zmedico | | | 15306 | Deprecate pordbapi.mysettings since the portdbapi.settings | | zmedico | alias is now supported by stable portage. | | 15307 | Bug #298141 - Make /etc/portage/sets relative to | | zmedico | PORTAGE_CONFIGROOT. Thanks to Martin Gysel (bearsh) | | | for this patch. | | 15308 | Fix @module-rebuild to support $ROOT. | | zmedico | | | 15309 | Clean up config incrementals handling. | | zmedico | | | 15310 | Simplify the conditional that triggers calculation of A and | | zmedico | AA variables. | | 15311 | Deallocate config instance when necessary, to avoid memory | | zmedico | leak when in prefetch mode. | | 15312 | Optimize parallel-fetch for the case where all files are | | zmedico | already fetched and have the correct size. In this case we | | | can avoid the expense of spawning ebuild(1). | | 15313 | Make sure the fetcher process correctly inherits | | zmedico | PORTAGE_CONFIGROOT. | | 15314 | Fix deprecated portdbapi.mysettings reference. | | zmedico | | | 15315 | Use stat rather than lstat since portage.fetch() creates | | zmedico | symlinks when PORTAGE_RO_DISTDIRS is used. | | 15316 | Use Package.use.enabled where appropriate. | | zmedico | | | 15317 | Bug #295197 - Output a newline after valid user input is | | zmedico | received. | | 15318 | Make verify_all() do checksums in sorted order by hash name. | | zmedico | | svn path=/main/branches/prefix/; revision=15320 --- diff --git a/bin/dispatch-conf b/bin/dispatch-conf index 3fdee4078..1adbae441 100755 --- a/bin/dispatch-conf +++ b/bin/dispatch-conf @@ -248,6 +248,8 @@ class dispatch: while True: c = getch() if c in valid_input: + sys.stdout.write('\n') + sys.stdout.flush() break if c == 'q': diff --git a/bin/filter-bash-environment.py b/bin/filter-bash-environment.py index f503ebe29..012756cb1 100755 --- a/bin/filter-bash-environment.py +++ b/bin/filter-bash-environment.py @@ -13,6 +13,8 @@ func_end_re = re.compile(r'^\}$') var_assign_re = re.compile(r'(^|^declare\s+-\S+\s+|^declare\s+|^export\s+)([^=\s]+)=("|\')?.*$') close_quote_re = re.compile(r'(\\"|"|\')\s*$') readonly_re = re.compile(r'^declare\s+-(\S*)r(\S*)\s+') +# declare without assignment +var_declare_re = re.compile(r'^declare(\s+-\S+)?\s+([^=\s]+)\s*$') def have_end_quote(quote, line): """ @@ -25,6 +27,21 @@ def have_end_quote(quote, line): return close_quote_match is not None and \ close_quote_match.group(1) == quote +def filter_declare_readonly_opt(line): + readonly_match = readonly_re.match(line) + if readonly_match is not None: + declare_opts = '' + for i in (1, 2): + group = readonly_match.group(i) + if group is not None: + declare_opts += group + if declare_opts: + line = 'declare -%s %s' % \ + (declare_opts, line[readonly_match.end():]) + else: + line = 'declare ' + line[readonly_match.end():] + return line + def filter_bash_environment(pattern, file_in, file_out): # Filter out any instances of the \1 character from variable values # since this character multiplies each time that the environment @@ -58,20 +75,20 @@ def filter_bash_environment(pattern, file_in, file_out): multi_line_quote = quote multi_line_quote_filter = filter_this if not filter_this: - readonly_match = readonly_re.match(line) - if readonly_match is not None: - declare_opts = "" - for i in (1, 2): - group = readonly_match.group(i) - if group is not None: - declare_opts += group - if declare_opts: - line = "declare -%s %s" % \ - (declare_opts, line[readonly_match.end():]) - else: - line = "declare " + line[readonly_match.end():] + line = filter_declare_readonly_opt(line) file_out.write(line.replace("\1", "")) continue + else: + declare_match = var_declare_re.match(line) + if declare_match is not None: + # declare without assignment + filter_this = pattern.match(declare_match.group(2)) \ + is not None + if not filter_this: + line = filter_declare_readonly_opt(line) + file_out.write(line) + continue + if here_doc_delim is not None: if here_doc_delim.match(line): here_doc_delim = None diff --git a/bin/repoman b/bin/repoman index 3edb2b1ee..13147bcab 100755 --- a/bin/repoman +++ b/bin/repoman @@ -89,8 +89,11 @@ bad = create_color_func("BAD") # A sane umask is needed for files that portage creates. os.umask(0o22) -repoman_settings = portage.config(local_config=False, - config_incrementals=portage.const.INCREMENTALS) +# Repoman sets it's own ACCEPT_KEYWORDS and we don't want it to +# behave incrementally. +repoman_incrementals = tuple(x for x in \ + portage.const.INCREMENTALS if x != 'ACCEPT_KEYWORDS') +repoman_settings = portage.config(local_config=False) repoman_settings.lock() if repoman_settings.get("NOCOLOR", "").lower() in ("yes", "true") or \ @@ -557,7 +560,7 @@ repoman_settings.categories = tuple(sorted( portage.util.stack_lists([categories], incremental=1))) del categories -portdb.mysettings = repoman_settings +portdb.settings = repoman_settings root_config = RootConfig(repoman_settings, trees[root], None) # We really only need to cache the metadata that's necessary for visibility # filtering. Anything else can be discarded to reduce memory consumption. @@ -1749,19 +1752,12 @@ for x in scanlist: if dep_settings is None: dep_settings = portage.config( config_profile_path=prof.abs_path, - config_incrementals=portage.const.INCREMENTALS, + config_incrementals=repoman_incrementals, local_config=False, env=env) if options.without_mask: dep_settings.pmaskdict.clear() arch_caches[prof.sub_path] = dep_settings - while True: - try: - # Protect ACCEPT_KEYWORDS from config.regenerate() - # (just in case) - dep_settings.incrementals.remove("ACCEPT_KEYWORDS") - except ValueError: - break xmatch_cache_key = (prof.sub_path, tuple(groups)) xcache = arch_xmatch_caches.get(xmatch_cache_key) @@ -1773,7 +1769,7 @@ for x in scanlist: arch_xmatch_caches[xmatch_cache_key] = xcache trees["/"]["porttree"].settings = dep_settings - portdb.mysettings = dep_settings + portdb.settings = dep_settings portdb.xcache = xcache # for package.use.mask support inside dep_check dep_settings.setcpv(pkg) diff --git a/cnf/sets.conf b/cnf/sets.conf index 44ff8340c..46c86e750 100644 --- a/cnf/sets.conf +++ b/cnf/sets.conf @@ -44,7 +44,7 @@ world-candidate = False [usersets] class = portage.sets.files.StaticFileSet multiset = true -directory = @PORTAGE_EPREFIX@/etc/portage/sets +directory = %(PORTAGE_CONFIGROOT)setc/portage/sets # Set to rebuild all packages that need a preserved lib that only remains due # to FEATURES=preserve-libs @@ -63,7 +63,7 @@ includes = bzr cvs darcs git mercurial subversion tla [module-rebuild] class = portage.sets.dbapi.OwnerSet world-candidate = False -files = /lib/modules +files = %(ROOT)slib/modules # Installed packages for which the highest visible ebuild # version is lower than the currently installed version. diff --git a/pym/_emerge/EbuildFetcher.py b/pym/_emerge/EbuildFetcher.py index c5d7a8b0d..25b45ce42 100644 --- a/pym/_emerge/EbuildFetcher.py +++ b/pym/_emerge/EbuildFetcher.py @@ -28,6 +28,12 @@ class EbuildFetcher(SpawnProcess): raise AssertionError("ebuild not found for '%s'" % self.pkg.cpv) settings = self.config_pool.allocate() settings.setcpv(self.pkg) + if self.prefetch and \ + self._prefetch_size_ok(portdb, settings, ebuild_path): + self.config_pool.deallocate(settings) + self.returncode = os.EX_OK + self.wait() + return # In prefetch mode, logging goes to emerge-fetch.log and the builddir # should not be touched since otherwise it could interfere with @@ -51,6 +57,7 @@ class EbuildFetcher(SpawnProcess): # along here so that they are correctly considered by # the config instance in the subproccess. fetch_env = os.environ.copy() + fetch_env['PORTAGE_CONFIGROOT'] = settings['PORTAGE_CONFIGROOT'] nocolor = settings.get("NOCOLOR") if nocolor is not None: @@ -75,8 +82,59 @@ class EbuildFetcher(SpawnProcess): self.args = fetch_args self.env = fetch_env + if self._build_dir is None: + # Free settings now since we only have a local reference. + self.config_pool.deallocate(settings) SpawnProcess._start(self) + def _prefetch_size_ok(self, portdb, settings, ebuild_path): + pkgdir = os.path.dirname(ebuild_path) + mytree = os.path.dirname(os.path.dirname(pkgdir)) + distdir = settings["DISTDIR"] + use = None + if not self.fetchall: + use = self.pkg.use.enabled + + try: + uri_map = portdb.getFetchMap(self.pkg.cpv, + useflags=use, mytree=mytree) + except portage.exception.InvalidDependString as e: + return False + + sizes = {} + for filename in uri_map: + # Use stat rather than lstat since portage.fetch() creates + # symlinks when PORTAGE_RO_DISTDIRS is used. + try: + st = os.stat(os.path.join(distdir, filename)) + except OSError: + return False + if st.st_size == 0: + return False + sizes[filename] = st.st_size + + digests = portage.Manifest(pkgdir, distdir).getTypeDigests("DIST") + for filename, actual_size in sizes.items(): + size = digests.get(filename, {}).get('size') + if size is None: + continue + if size != actual_size: + return False + + # All files are present and sizes are ok. In this case the normal + # fetch code will be skipped, so we need to generate equivalent + # output here. + if self.logfile is not None: + f = codecs.open(_unicode_encode(self.logfile, + encoding=_encodings['fs'], errors='strict'), + mode='a', encoding=_encodings['content'], errors='replace') + for filename in uri_map: + f.write((' * %s size ;-) ...' % \ + filename).ljust(73) + '[ ok ]\n') + f.close() + + return True + def _pipe(self, fd_pipes): """When appropriate, use a pty so that fetcher progress bars, like wget has, will work properly.""" diff --git a/pym/_emerge/create_world_atom.py b/pym/_emerge/create_world_atom.py index 7ded8af13..1618905c9 100644 --- a/pym/_emerge/create_world_atom.py +++ b/pym/_emerge/create_world_atom.py @@ -81,7 +81,7 @@ def create_world_atom(pkg, args_set, root_config): # System virtuals aren't safe to exclude from world since they can # match multiple old-style virtuals but only one of them will be # pulled in by update or depclean. - providers = portdb.mysettings.getvirtuals().get( + providers = portdb.settings.getvirtuals().get( portage.dep_getkey(system_atom)) if providers and len(providers) == 1 and \ portage.dep_getkey(providers[0]) == cp: diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py index af4ec2e9c..e3d085a9d 100644 --- a/pym/_emerge/main.py +++ b/pym/_emerge/main.py @@ -1081,7 +1081,7 @@ def repo_name_duplicate_check(trees): for root, root_trees in trees.items(): if 'porttree' in root_trees: portdb = root_trees['porttree'].dbapi - if portdb.mysettings.get('PORTAGE_REPO_DUPLICATE_WARN') != '0': + if portdb.settings.get('PORTAGE_REPO_DUPLICATE_WARN') != '0': for repo_name, paths in portdb._ignored_repos: k = (root, repo_name, portdb.getRepositoryPath(repo_name)) ignored_repos.setdefault(k, []).extend(paths) diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index c442a1168..8ef9ebb2e 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -2124,7 +2124,8 @@ class config(object): #getting categories from an external file now categories = [grabfile(os.path.join(x, "categories")) for x in locations] self.categories = tuple(sorted( - stack_lists(categories, incremental=1))) + x for x in stack_lists(categories, incremental=1) + if dbapi._category_re.match(x) is not None)) del categories archlist = [grabfile(os.path.join(x, "arch.list")) for x in locations] @@ -7211,7 +7212,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, mydo not in ("digest", "manifest") and "noauto" not in features) alist = mysettings.configdict["pkg"].get("A") aalist = mysettings.configdict["pkg"].get("AA") - if need_distfiles or alist is None or aalist is None: + if alist is None or aalist is None: # Make sure we get the correct tree in case there are overlays. mytree = os.path.realpath( os.path.dirname(os.path.dirname(mysettings["O"]))) diff --git a/pym/portage/checksum.py b/pym/portage/checksum.py index 6b849d5d7..badde3daf 100644 --- a/pym/portage/checksum.py +++ b/pym/portage/checksum.py @@ -186,7 +186,7 @@ def verify_all(filename, mydict, calc_prelink=0, strict=0): got = " ".join(got) return False, (_("Insufficient data for checksum verification"), got, expected) - for x in mydict: + for x in sorted(mydict): if x == "size": continue elif x in hashfunc_map: diff --git a/pym/portage/dbapi/__init__.py b/pym/portage/dbapi/__init__.py index 53358c829..c758491f1 100644 --- a/pym/portage/dbapi/__init__.py +++ b/pym/portage/dbapi/__init__.py @@ -171,7 +171,7 @@ class dbapi(object): continue else: # Check masked and forced flags for repoman. - mysettings = getattr(self, "mysettings", None) + mysettings = getattr(self, 'settings', None) if mysettings is not None and not mysettings.local_config: pkg = "%s:%s" % (cpv, slot) diff --git a/pym/portage/dbapi/porttree.py b/pym/portage/dbapi/porttree.py index d5ecce987..4cf7d410d 100644 --- a/pym/portage/dbapi/porttree.py +++ b/pym/portage/dbapi/porttree.py @@ -125,17 +125,22 @@ class portdbapi(dbapi): _use_mutable = True def _get_settings(self): - return self.mysettings + warnings.warn("Use portdbapi.settings insead of portdbapi.mysettings", + DeprecationWarning) + return self.settings def _set_settings(self, settings): - self.mysettings = settings + warnings.warn("Use portdbapi.settings insead of portdbapi.mysettings", + DeprecationWarning) + self.settings = settings def _del_settings (self): - del self.mysettings + warnings.warn("Use portdbapi.settings insead of portdbapi.mysettings", + DeprecationWarning) + del self.settings - settings = property(_get_settings, _set_settings, _del_settings, - "Define self.settings as an alias for self.mysettings, " + \ - "for conformity with other dbapi classes.") + mysettings = property(_get_settings, _set_settings, _del_settings, + "Deprecated self.mysettings, only for backward compatibility") @property def _categories(self): @@ -152,12 +157,12 @@ class portdbapi(dbapi): from portage import config if mysettings: - self.mysettings = mysettings + self.settings = mysettings else: from portage import settings - self.mysettings = config(clone=settings) + self.settings = config(clone=settings) - porttree_root = self.mysettings['PORTDIR'] + porttree_root = self.settings['PORTDIR'] # always show this warning after this parameter # is unused in stable portage @@ -172,8 +177,8 @@ class portdbapi(dbapi): # is generated by the depend phase. It's safest to use a clone for # this purpose because doebuild makes many changes to the config # instance that is passed in. - self.doebuild_settings = config(clone=self.mysettings) - self.depcachedir = os.path.realpath(self.mysettings.depcachedir) + self.doebuild_settings = config(clone=self.settings) + self.depcachedir = os.path.realpath(self.settings.depcachedir) if os.environ.get("SANDBOX_ON") == "1": # Make api consumers exempt from sandbox violations @@ -186,7 +191,7 @@ class portdbapi(dbapi): porttrees = [os.path.realpath(porttree_root)] porttrees.extend(os.path.realpath(x) for x in \ - self.mysettings.get('PORTDIR_OVERLAY', '').split()) + self.settings.get('PORTDIR_OVERLAY', '').split()) treemap = {} repository_map = {} self.treemap = treemap @@ -242,7 +247,7 @@ class portdbapi(dbapi): self._have_root_eclass_dir = os.path.isdir( os.path.join(self.porttree_root, "eclass")) - self.metadbmodule = self.mysettings.load_best_module("portdbapi.metadbmodule") + self.metadbmodule = self.settings.load_best_module("portdbapi.metadbmodule") #if the portdbapi is "frozen", then we assume that we can cache everything (that no updates to it are happening) self.xcache = {} @@ -250,7 +255,7 @@ class portdbapi(dbapi): self._repo_info = {} eclass_dbs = {porttree_root : self.eclassdb} - local_repo_configs = self.mysettings._local_repo_configs + local_repo_configs = self.settings._local_repo_configs default_loc_repo_config = None repo_aliases = {} if local_repo_configs is not None: @@ -265,7 +270,7 @@ class portdbapi(dbapi): "'%s' alias in " \ "'%s'\n") % (alias, repo_name, overridden_alias, - self.mysettings._local_repo_conf_path), + self.settings._local_repo_conf_path), level=logging.WARNING, noiselevel=-1) repo_aliases[alias] = repo_name @@ -324,7 +329,7 @@ class portdbapi(dbapi): writemsg_level(_("Unavailable repository '%s' " \ "referenced by eclass-overrides entry in " \ "'%s'\n") % (other_name, - self.mysettings._local_repo_conf_path), + self.settings._local_repo_conf_path), level=logging.ERROR, noiselevel=-1) continue porttrees.append(other_path) @@ -342,7 +347,7 @@ class portdbapi(dbapi): self._repo_info[path] = _repo_info(repo_name, path, eclass_db) - self.auxdbmodule = self.mysettings.load_best_module("portdbapi.auxdbmodule") + self.auxdbmodule = self.settings.load_best_module("portdbapi.auxdbmodule") self.auxdb = {} self._pregen_auxdb = {} self._init_cache_dirs() @@ -379,7 +384,7 @@ class portdbapi(dbapi): self.depcachedir, x, filtered_auxdbkeys, **cache_kwargs) if self.auxdbmodule is metadata_overlay.database: self.auxdb[x].db_ro.ec = self._repo_info[x].eclass_db - if "metadata-transfer" not in self.mysettings.features: + if "metadata-transfer" not in self.settings.features: for x in self.porttrees: if x in self._pregen_auxdb: continue @@ -806,7 +811,7 @@ class portdbapi(dbapi): if myebuild is None: raise AssertionError("ebuild not found for '%s'" % mypkg) pkgdir = os.path.dirname(myebuild) - mf = Manifest(pkgdir, self.mysettings["DISTDIR"]) + mf = Manifest(pkgdir, self.settings["DISTDIR"]) checksums = mf.getDigests() if not checksums: if debug: @@ -821,7 +826,7 @@ class portdbapi(dbapi): if debug: writemsg(_("[bad digest]: missing %(file)s for %(pkg)s\n") % {"file":myfile, "pkg":mypkg}) continue - file_path = os.path.join(self.mysettings["DISTDIR"], myfile) + file_path = os.path.join(self.settings["DISTDIR"], myfile) mystat = None try: mystat = os.stat(file_path) @@ -851,7 +856,7 @@ class portdbapi(dbapi): if myebuild is None: raise AssertionError("ebuild not found for '%s'" % mypkg) pkgdir = os.path.dirname(myebuild) - mf = Manifest(pkgdir, self.mysettings["DISTDIR"]) + mf = Manifest(pkgdir, self.settings["DISTDIR"]) mysums = mf.getDigests() failures = {} @@ -862,7 +867,7 @@ class portdbapi(dbapi): else: try: ok, reason = portage.checksum.verify_all( - os.path.join(self.mysettings["DISTDIR"], x), mysums[x]) + os.path.join(self.settings["DISTDIR"], x), mysums[x]) except FileNotFound as e: ok = False reason = _("File Not Found: '%s'") % (e,) @@ -887,7 +892,7 @@ class portdbapi(dbapi): def cp_all(self): "returns a list of all keys in our tree" d = {} - for x in self.mysettings.categories: + for x in self.settings.categories: for oroot in self.porttrees: for y in listdir(oroot+"/"+x, EmptyOnError=1, ignorecvs=1, dirsonly=1): if not self._pkg_dir_name_re.match(y) or \ @@ -948,7 +953,7 @@ class portdbapi(dbapi): if invalid_category and d: writemsg(_("\n!!! '%s' has a category that is not listed in " \ "%setc/portage/categories\n") % \ - (mycp, self.mysettings["PORTAGE_CONFIGROOT"]), noiselevel=-1) + (mycp, self.settings["PORTAGE_CONFIGROOT"]), noiselevel=-1) mylist = [] else: mylist = list(d) @@ -986,7 +991,7 @@ class portdbapi(dbapi): if not mydep: #this stuff only runs on first call of xmatch() #create mydep, mykey from origdep - mydep = dep_expand(origdep, mydb=self, settings=self.mysettings) + mydep = dep_expand(origdep, mydb=self, settings=self.settings) mykey = mydep.cp if level == "list-visible": @@ -1015,7 +1020,7 @@ class portdbapi(dbapi): else: mylist = match_from_list(mydep, self.cp_list(mykey)) myval = "" - settings = self.mysettings + settings = self.settings local_config = settings.local_config aux_keys = list(self._aux_cache_keys) if level == "minimum-visible": @@ -1102,8 +1107,8 @@ class portdbapi(dbapi): db_keys = ["SLOT"] visible = [] - getMaskAtom = self.mysettings._getMaskAtom - getProfileMaskAtom = self.mysettings._getProfileMaskAtom + getMaskAtom = self.settings._getMaskAtom + getProfileMaskAtom = self.settings._getProfileMaskAtom for cpv in mylist: try: metadata = dict(zip(db_keys, self.aux_get(cpv, db_keys))) @@ -1127,9 +1132,9 @@ class portdbapi(dbapi): newlist=[] aux_keys = list(self._aux_cache_keys) metadata = {} - local_config = self.mysettings.local_config - chost = self.mysettings.get('CHOST', '') - accept_chost = self.mysettings._accept_chost + local_config = self.settings.local_config + chost = self.settings.get('CHOST', '') + accept_chost = self.settings._accept_chost for mycpv in mylist: metadata.clear() try: @@ -1147,7 +1152,7 @@ class portdbapi(dbapi): continue if _eapi_is_deprecated(eapi): continue - if self.mysettings._getMissingKeywords(mycpv, metadata): + if self.settings._getMissingKeywords(mycpv, metadata): continue if local_config: metadata['CHOST'] = chost @@ -1158,9 +1163,9 @@ class portdbapi(dbapi): self.doebuild_settings.setcpv(mycpv, mydb=metadata) metadata['USE'] = self.doebuild_settings['PORTAGE_USE'] try: - if self.mysettings._getMissingLicenses(mycpv, metadata): + if self.settings._getMissingLicenses(mycpv, metadata): continue - if self.mysettings._getMissingProperties(mycpv, metadata): + if self.settings._getMissingProperties(mycpv, metadata): continue except InvalidDependString: continue diff --git a/pym/portage/news.py b/pym/portage/news.py index c178a8c48..a1ce42246 100644 --- a/pym/portage/news.py +++ b/pym/portage/news.py @@ -59,9 +59,9 @@ class NewsManager(object): portdir = portdb.porttree_root profiles_base = os.path.join(portdir, 'profiles') + os.path.sep profile_path = None - if portdb.mysettings.profile_path: + if portdb.settings.profile_path: profile_path = normalize_path( - os.path.realpath(portdb.mysettings.profile_path)) + os.path.realpath(portdb.settings.profile_path)) if profile_path.startswith(profiles_base): profile_path = profile_path[len(profiles_base):] self._profile_path = profile_path diff --git a/pym/portage/sets/__init__.py b/pym/portage/sets/__init__.py index 78f2628bd..65ade2e54 100644 --- a/pym/portage/sets/__init__.py +++ b/pym/portage/sets/__init__.py @@ -34,7 +34,11 @@ class SetConfigError(Exception): class SetConfig(object): def __init__(self, paths, settings, trees): - self._parser = SafeConfigParser() + self._parser = SafeConfigParser( + defaults={ + "PORTAGE_CONFIGROOT" : settings["PORTAGE_CONFIGROOT"], + "ROOT" : settings["ROOT"], + }) self._parser.read(paths) self.errors = [] self.psets = {}