From 58050ee5cbbc882456dac0cd5e054e540845cd85 Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Fri, 14 Aug 2009 20:54:36 +0000 Subject: [PATCH] Merged from trunk -r14008:14018 | 14009 | Use _unicode_encode() where appropriate. | | zmedico | | | 14010 | Call _ensure_default_encoding() ASAP since writemsg() might | | zmedico | not work without it. | | 14011 | Use _unicode_encode() and _unicode_decode() where | | zmedico | appropriate. | | 14012 | Bug #281133 - Fix the here-document code inside run_checks() | | zmedico | to properly detect the end of a here-document when it is | | | indented by whitespace. | | 14013 | Use r'' for regex. | | zmedico | | | 14014 | Set a limit of 30 backtracking attempts, since it's possible | | zmedico | for it to go out of control and take an unreasonable amount | | | of time. | | 14015 | Bug #281355 - Handle IndexError inside | | zmedico | reconstruct_eclasses(). | | 14016 | Add py3k support to _unicode_decode(). | | zmedico | | | 14017 | Add support for unwrapped/overridden attributes in | | zmedico | _unicode_module_wrapper(). | | 14018 | Add support for an 'encoding' parameter to | | zmedico | _unicode_module_wrapper(). This will allow creation of | | | specialize wrappers for merge/unmerge, in case the value of | | | sys.getfilesystemencoding() is something other than utf_8. | svn path=/main/branches/prefix/; revision=14045 --- pym/_emerge/depgraph.py | 7 +- pym/portage/__init__.py | 366 ++++++++++++++++++---------------- pym/portage/cache/template.py | 3 + pym/portage/util.py | 4 +- pym/repoman/checks.py | 2 +- 5 files changed, 206 insertions(+), 176 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 31c1848bc..2f5774e1c 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -4949,9 +4949,10 @@ def backtrack_depgraph(settings, trees, myopts, myparams, """ Raises PackageSetNotFound if myfiles contains a missing package set. """ + backtrack_max = 30 runtime_pkg_mask = None allow_backtracking = True - backtracked = False + backtracked = 0 frozen_config = _frozen_depgraph_config(settings, trees, myopts, spinner) while True: @@ -4961,9 +4962,9 @@ def backtrack_depgraph(settings, trees, myopts, myparams, runtime_pkg_mask=runtime_pkg_mask) success, favorites = mydepgraph.select_files(myfiles) if not success: - if mydepgraph.need_restart(): + if mydepgraph.need_restart() and backtracked < backtrack_max: runtime_pkg_mask = mydepgraph.get_runtime_pkg_mask() - backtracked = True + backtracked += 1 elif backtracked and allow_backtracking: # Backtracking failed, so disable it and do # a plain dep calculation + error message. diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index b9418f022..0c068b698 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -118,14 +118,19 @@ except ImportError, e: sys.stderr.write(" "+str(e)+"\n\n") raise -def _unicode_encode(s): +def _unicode_encode(s, encoding='utf_8', errors='replace'): if isinstance(s, unicode): - s = s.encode('utf_8', 'replace') + s = s.encode(encoding, errors) return s -def _unicode_decode(s): - if not isinstance(s, unicode) and isinstance(s, basestring): - s = unicode(s, encoding='utf_8', errors='replace') +def _unicode_decode(s, encoding='utf_8', errors='replace'): + if not isinstance(s, unicode): + if sys.hexversion < 0x3000000: + if isinstance(s, basestring): + s = unicode(s, encoding=encoding, errors=errors) + else: + if isinstance(s, bytes): + s = unicode(s, encoding=encoding, errors=errors) return s class _unicode_func_wrapper(object): @@ -133,16 +138,19 @@ class _unicode_func_wrapper(object): Wraps a function, converts arguments from unicode to bytes, and return values to unicode from bytes. """ - __slots__ = ('_func',) + __slots__ = ('_func', '_encoding') - def __init__(self, func): + def __init__(self, func, encoding='utf_8'): self._func = func + self._encoding = encoding def __call__(self, *args, **kwargs): + encoding = self._encoding wrapped_args = [_unicode_encode(x) for x in args] if kwargs: - wrapped_kwargs = dict((_unicode_encode(k), _unicode_encode(v)) \ + wrapped_kwargs = dict((_unicode_encode(k, encoding=encoding), + _unicode_encode(v, encoding=encoding)) \ for k, v in kwargs.iteritems()) else: wrapped_kwargs = {} @@ -151,11 +159,12 @@ class _unicode_func_wrapper(object): if isinstance(rval, (basestring, list, tuple)): if isinstance(rval, basestring): - rval = _unicode_decode(rval) + rval = _unicode_decode(rval, encoding=encoding) elif isinstance(rval, list): - rval = [_unicode_decode(x) for x in rval] + rval = [_unicode_decode(x, encoding=encoding) for x in rval] elif isinstance(rval, tuple): - rval = tuple(_unicode_decode(x) for x in rval) + rval = tuple(_unicode_decode(x, encoding=encoding) \ + for x in rval) return rval @@ -163,19 +172,29 @@ class _unicode_module_wrapper(object): """ Wraps a module and wraps all functions with _unicode_func_wrapper. """ - __slots__ = ('_mod',) + __slots__ = ('_mod', '_encoding', '_overrides') - def __init__(self, mod): + def __init__(self, mod, encoding='utf_8', overrides=None): object.__setattr__(self, '_mod', mod) + object.__setattr__(self, '_encoding', encoding) + object.__setattr__(self, '_overrides', overrides) def __getattribute__(self, attr): result = getattr(object.__getattribute__(self, '_mod'), attr) - if isinstance(result, type): + encoding = object.__getattribute__(self, '_encoding') + overrides = object.__getattribute__(self, '_overrides') + override = None + if overrides is not None: + override = overrides.get(id(result)) + if override is not None: + result = override + elif isinstance(result, type): pass elif type(result) is types.ModuleType: - result = _unicode_module_wrapper(result) + result = _unicode_module_wrapper(result, + encoding=encoding, overrides=overrides) elif hasattr(result, '__call__'): - result = _unicode_func_wrapper(result) + result = _unicode_func_wrapper(result, encoding=encoding) return result if sys.hexversion >= 0x3000000: @@ -185,7 +204,7 @@ if sys.hexversion >= 0x3000000: return mod import os -os = _unicode_module_wrapper(os) +os = _unicode_module_wrapper(os, overrides={id(os.read):os.read}) import shutil shutil = _unicode_module_wrapper(shutil) @@ -194,7 +213,7 @@ shutil = _unicode_module_wrapper(shutil) try: import portage._selinux as selinux except OSError, e: - writemsg("!!! SELinux not loaded: %s\n" % str(e), noiselevel=-1) + sys.stderr.write("!!! SELinux not loaded: %s\n" % str(e)) del e except ImportError: pass @@ -205,6 +224,138 @@ from portage.manifest import Manifest # END OF IMPORTS -- END OF IMPORTS -- END OF IMPORTS -- END OF IMPORTS -- END # =========================================================================== +def _gen_missing_encodings(missing_encodings): + + encodings = {} + + if 'ascii' in missing_encodings: + + class AsciiIncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.ascii_encode(input, self.errors)[0] + + class AsciiIncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.ascii_decode(input, self.errors)[0] + + class AsciiStreamWriter(codecs.StreamWriter): + encode = codecs.ascii_encode + + class AsciiStreamReader(codecs.StreamReader): + decode = codecs.ascii_decode + + codec_info = codecs.CodecInfo( + name='ascii', + encode=codecs.ascii_encode, + decode=codecs.ascii_decode, + incrementalencoder=AsciiIncrementalEncoder, + incrementaldecoder=AsciiIncrementalDecoder, + streamwriter=AsciiStreamWriter, + streamreader=AsciiStreamReader, + ) + + for alias in ('ascii', '646', 'ansi_x3.4_1968', 'ansi_x3_4_1968', + 'ansi_x3.4_1986', 'cp367', 'csascii', 'ibm367', 'iso646_us', + 'iso_646.irv_1991', 'iso_ir_6', 'us', 'us_ascii'): + encodings[alias] = codec_info + + if 'utf_8' in missing_encodings: + + def utf8decode(input, errors='strict'): + return codecs.utf_8_decode(input, errors, True) + + class Utf8IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.utf_8_encode(input, self.errors)[0] + + class Utf8IncrementalDecoder(codecs.BufferedIncrementalDecoder): + _buffer_decode = codecs.utf_8_decode + + class Utf8StreamWriter(codecs.StreamWriter): + encode = codecs.utf_8_encode + + class Utf8StreamReader(codecs.StreamReader): + decode = codecs.utf_8_decode + + codec_info = codecs.CodecInfo( + name='utf-8', + encode=codecs.utf_8_encode, + decode=utf8decode, + incrementalencoder=Utf8IncrementalEncoder, + incrementaldecoder=Utf8IncrementalDecoder, + streamreader=Utf8StreamReader, + streamwriter=Utf8StreamWriter, + ) + + for alias in ('utf_8', 'u8', 'utf', 'utf8', 'utf8_ucs2', 'utf8_ucs4'): + encodings[alias] = codec_info + + return encodings + +def _ensure_default_encoding(): + """ + The python that's inside stage 1 or 2 is built with a minimal + configuration which does not include the /usr/lib/pythonX.Y/encodings + directory. This results in error like the following: + + LookupError: no codec search functions registered: can't find encoding + + In order to solve this problem, detect it early and manually register + a search function for the ascii and utf_8 codecs. Starting with python-3.0 + this problem is more noticeable because of stricter handling of encoding + and decoding between strings of characters and bytes. + """ + + default_fallback = 'utf_8' + default_encoding = sys.getdefaultencoding().lower().replace('-', '_') + required_encodings = set(['ascii', 'utf_8']) + required_encodings.add(default_encoding) + missing_encodings = set() + for codec_name in required_encodings: + try: + codecs.lookup(codec_name) + except LookupError: + missing_encodings.add(codec_name) + + if not missing_encodings: + return + + encodings = _gen_missing_encodings(missing_encodings) + + if default_encoding in missing_encodings and \ + default_encoding not in encodings: + # Make the fallback codec correspond to whatever name happens + # to be returned by sys.getdefaultencoding(). + + try: + encodings[default_encoding] = codecs.lookup(default_fallback) + except LookupError: + encodings[default_encoding] = encodings[default_fallback] + + def search_function(name): + name = name.lower() + name = name.replace('-', '_') + codec_info = encodings.get(name) + if codec_info is not None: + return codecs.CodecInfo( + name=codec_info.name, + encode=codec_info.encode, + decode=codec_info.decode, + incrementalencoder=codec_info.incrementalencoder, + incrementaldecoder=codec_info.incrementaldecoder, + streamreader=codec_info.streamreader, + streamwriter=codec_info.streamwriter, + ) + return None + + codecs.register(search_function) + + del codec_name, default_encoding, default_fallback, missing_encodings, \ + required_encodings, search_function + +# Do this ASAP since writemsg() might not work without it. +_ensure_default_encoding() + def _shell_quote(s): """ Quote a string in double-quotes and use backslashes to @@ -807,7 +958,7 @@ def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None, ldsoconf_path = os.path.join(target_root, EPREFIX_LSTRIP, "etc", "ld.so.conf") try: - myld = codecs.open(ldsoconf_path, mode='r', + myld = codecs.open(_unicode_encode(ldsoconf_path), mode='r', encoding='utf_8', errors='replace') myldlines=myld.readlines() myld.close() @@ -989,7 +1140,8 @@ def ExtractKernelVersion(base_dir): lines = [] pathname = os.path.join(base_dir, 'Makefile') try: - f = open(pathname, 'r') + f = codecs.open(_unicode_encode(pathname), mode='r', + encoding='utf_8', errors='replace') except OSError, details: return (None, str(details)) except IOError, details: @@ -1451,7 +1603,9 @@ class config(object): parentsFile = os.path.join(currentPath, "parent") eapi_file = os.path.join(currentPath, "eapi") try: - eapi = open(eapi_file).readline().strip() + eapi = codecs.open(_unicode_encode(eapi_file), + mode='r', encoding='utf_8', errors='replace' + ).readline().strip() except IOError: pass else: @@ -1797,7 +1951,8 @@ class config(object): repo_conf_parser = SafeConfigParser() try: repo_conf_parser.readfp( - codecs.open(self._local_repo_conf_path, mode='r', + codecs.open( + _unicode_encode(self._local_repo_conf_path), mode='r', encoding='utf_8', errors='replace')) except EnvironmentError, e: if e.errno != errno.ENOENT: @@ -3735,7 +3890,7 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, fakero return mypids if logfile: - log_file = open(logfile, mode='ab') + log_file = open(_unicode_encode(logfile), mode='ab') stdout_file = os.fdopen(os.dup(fd_pipes_orig[1]), 'wb') master_file = os.fdopen(master_fd, 'rb') iwtd = [master_file] @@ -4717,7 +4872,10 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks", # Fetch failed... Try the next one... Kill 404 files though. if (mystat[stat.ST_SIZE]<100000) and (len(myfile)>4) and not ((myfile[-5:]==".html") or (myfile[-4:]==".htm")): html404=re.compile(".*(not found|404).*",re.I|re.M) - if html404.search(open(mysettings["DISTDIR"]+"/"+myfile).read()): + if html404.search(codecs.open( + _unicode_encode(myfile_path), mode='r', + encoding='utf_8', errors='replace' + ).read()): try: os.unlink(mysettings["DISTDIR"]+"/"+myfile) writemsg(">>> Deleting invalid distfile. (Improper 404 redirect from server.)\n") @@ -5266,7 +5424,7 @@ def _check_build_log(mysettings, out=None): if logfile is None: return try: - f = codecs.open(logfile, mode='r', + f = codecs.open(_unicode_encode(logfile), mode='r', encoding='utf_8', errors='replace') except EnvironmentError: return @@ -5427,8 +5585,8 @@ def _post_src_install_uid_fix(mysettings): mode=mystat.st_mode, stat_cached=mystat, follow_links=False) - open(os.path.join(mysettings['PORTAGE_BUILDDIR'], - 'build-info', 'SIZE'), 'w').write(str(size) + '\n') + open(_unicode_encode(os.path.join(mysettings['PORTAGE_BUILDDIR'], + 'build-info', 'SIZE')), 'w').write(str(size) + '\n') if bsd_chflags: # Restore all of the flags saved above. @@ -5664,7 +5822,8 @@ def doebuild_environment(myebuild, mydo, myroot, mysettings, debug, use_cache, m # From parse-eapi-glep-55 above. pass elif 'parse-eapi-ebuild-head' in mysettings.features: - eapi = _parse_eapi_ebuild_head(codecs.open(ebuild_path, + eapi = _parse_eapi_ebuild_head( + codecs.open(_unicode_encode(ebuild_path), mode='r', encoding='utf_8', errors='replace')) if eapi is not None: @@ -5831,13 +5990,14 @@ def _adjust_perms_msg(settings, msg): if background and log_path is not None: try: - log_file = open(log_path, 'a') + log_file = codecs.open(_unicode_encode(log_path), mode='a', + encoding='utf_8', errors='replace') except IOError: def write(msg): pass else: def write(msg): - log_file.write(msg) + log_file.write(_unicode_decode(msg)) log_file.flush() try: @@ -5999,9 +6159,7 @@ def _prepare_workdir(mysettings): os.access(mysettings["PORT_LOGDIR"], os.W_OK): logid_path = os.path.join(mysettings["PORTAGE_BUILDDIR"], ".logid") if not os.path.exists(logid_path): - f = open(logid_path, "w") - f.close() - del f + open(_unicode_encode(logid_path), 'w') logid_time = time.strftime("%Y%m%d-%H%M%S", time.gmtime(os.stat(logid_path).st_mtime)) mysettings["PORTAGE_LOG_FILE"] = os.path.join( @@ -6479,7 +6637,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, # This is a signal to ebuild.sh, so that it knows to filter # out things like SANDBOX_{DENY,PREDICT,READ,WRITE} that # would be preserved between normal phases. - open(env_file + ".raw", "w") + open(_unicode_encode(env_file + '.raw'), 'w') else: writemsg(("!!! Error extracting saved " + \ "environment: '%s'\n") % \ @@ -8105,7 +8263,7 @@ def pkgmerge(mytbz2, myroot, mysettings, mydbapi=None, xptbz2.unpackinfo(infloc) mysettings.setcpv(mycat + "/" + mypkg, mydb=mydbapi) # Store the md5sum in the vdb. - fp = open(os.path.join(infloc, "BINPKGMD5"), "w") + fp = open(_unicode_encode(os.path.join(infloc, 'BINPKGMD5')), 'w') fp.write(str(portage.checksum.perform_md5(mytbz2))+"\n") fp.close() @@ -8192,9 +8350,8 @@ def deprecated_profile_check(settings=None): DEPRECATED_PROFILE_FILE.lstrip(os.sep)) if not os.access(deprecated_profile_file, os.R_OK): return False - deprecatedfile = open(deprecated_profile_file, "r") - dcontent = deprecatedfile.readlines() - deprecatedfile.close() + dcontent = codecs.open(_unicode_encode(deprecated_profile_file), + mode='r', encoding='utf_8', errors='replace').readlines() writemsg(colorize("BAD", "\n!!! Your current profile is " + \ "deprecated and not supported anymore.") + "\n", noiselevel=-1) if not dcontent: @@ -8248,135 +8405,6 @@ def portageexit(): atexit_register(portageexit) -def _gen_missing_encodings(missing_encodings): - - encodings = {} - - if 'ascii' in missing_encodings: - - class AsciiIncrementalEncoder(codecs.IncrementalEncoder): - def encode(self, input, final=False): - return codecs.ascii_encode(input, self.errors)[0] - - class AsciiIncrementalDecoder(codecs.IncrementalDecoder): - def decode(self, input, final=False): - return codecs.ascii_decode(input, self.errors)[0] - - class AsciiStreamWriter(codecs.StreamWriter): - encode = codecs.ascii_encode - - class AsciiStreamReader(codecs.StreamReader): - decode = codecs.ascii_decode - - codec_info = codecs.CodecInfo( - name='ascii', - encode=codecs.ascii_encode, - decode=codecs.ascii_decode, - incrementalencoder=AsciiIncrementalEncoder, - incrementaldecoder=AsciiIncrementalDecoder, - streamwriter=AsciiStreamWriter, - streamreader=AsciiStreamReader, - ) - - for alias in ('ascii', '646', 'ansi_x3.4_1968', 'ansi_x3_4_1968', - 'ansi_x3.4_1986', 'cp367', 'csascii', 'ibm367', 'iso646_us', - 'iso_646.irv_1991', 'iso_ir_6', 'us', 'us_ascii'): - encodings[alias] = codec_info - - if 'utf_8' in missing_encodings: - - def utf8decode(input, errors='strict'): - return codecs.utf_8_decode(input, errors, True) - - class Utf8IncrementalEncoder(codecs.IncrementalEncoder): - def encode(self, input, final=False): - return codecs.utf_8_encode(input, self.errors)[0] - - class Utf8IncrementalDecoder(codecs.BufferedIncrementalDecoder): - _buffer_decode = codecs.utf_8_decode - - class Utf8StreamWriter(codecs.StreamWriter): - encode = codecs.utf_8_encode - - class Utf8StreamReader(codecs.StreamReader): - decode = codecs.utf_8_decode - - codec_info = codecs.CodecInfo( - name='utf-8', - encode=codecs.utf_8_encode, - decode=utf8decode, - incrementalencoder=Utf8IncrementalEncoder, - incrementaldecoder=Utf8IncrementalDecoder, - streamreader=Utf8StreamReader, - streamwriter=Utf8StreamWriter, - ) - - for alias in ('utf_8', 'u8', 'utf', 'utf8', 'utf8_ucs2', 'utf8_ucs4'): - encodings[alias] = codec_info - - return encodings - -def _ensure_default_encoding(): - """ - The python that's inside stage 1 or 2 is built with a minimal - configuration which does not include the /usr/lib/pythonX.Y/encodings - directory. This results in error like the following: - - LookupError: no codec search functions registered: can't find encoding - - In order to solve this problem, detect it early and manually register - a search function for the ascii and utf_8 codecs. Starting with python-3.0 - this problem is more noticeable because of stricter handling of encoding - and decoding between strings of characters and bytes. - """ - - default_fallback = 'utf_8' - default_encoding = sys.getdefaultencoding().lower().replace('-', '_') - required_encodings = set(['ascii', 'utf_8']) - required_encodings.add(default_encoding) - missing_encodings = set() - for codec_name in required_encodings: - try: - codecs.lookup(codec_name) - except LookupError: - missing_encodings.add(codec_name) - - if not missing_encodings: - return - - encodings = _gen_missing_encodings(missing_encodings) - - if default_encoding in missing_encodings and \ - default_encoding not in encodings: - # Make the fallback codec correspond to whatever name happens - # to be returned by sys.getdefaultencoding(). - - try: - encodings[default_encoding] = codecs.lookup(default_fallback) - except LookupError: - encodings[default_encoding] = encodings[default_fallback] - - def search_function(name): - name = name.lower() - name = name.replace('-', '_') - codec_info = encodings.get(name) - if codec_info is not None: - return codecs.CodecInfo( - name=codec_info.name, - encode=codec_info.encode, - decode=codec_info.decode, - incrementalencoder=codec_info.incrementalencoder, - incrementaldecoder=codec_info.incrementaldecoder, - streamreader=codec_info.streamreader, - streamwriter=codec_info.streamwriter, - ) - return None - - codecs.register(search_function) - - del codec_name, default_encoding, default_fallback, missing_encodings, \ - required_encodings, search_function - def _global_updates(trees, prev_mtimes): """ Perform new global updates if they exist in $PORTDIR/profiles/updates/. @@ -8527,7 +8555,7 @@ class MtimeDB(dict): def _load(self, filename): try: - f = open(filename, 'rb') + f = open(_unicode_encode(filename), 'rb') mypickle = pickle.Unpickler(f) try: mypickle.find_global = None @@ -8763,8 +8791,6 @@ if True: "flushmtimedb"): globals()[k] = _LegacyGlobalProxy(k) - _ensure_default_encoding() - # Clear the cache dircache={} diff --git a/pym/portage/cache/template.py b/pym/portage/cache/template.py index e78216338..97adb1058 100644 --- a/pym/portage/cache/template.py +++ b/pym/portage/cache/template.py @@ -224,6 +224,9 @@ def reconstruct_eclasses(cpv, eclass_string): # The old format contains paths that will be discarded. for x in xrange(0, len(eclasses), 3): d[eclasses[x]] = (eclasses[x + 1], long(eclasses[x + 2])) + except IndexError: + raise cache_errors.CacheCorruption(cpv, + "_eclasses_ was of invalid len %i" % len(eclasses)) except ValueError: raise cache_errors.CacheCorruption(cpv, "_eclasses_ mtime conversion to long failed") del eclasses diff --git a/pym/portage/util.py b/pym/portage/util.py index 3d6e8e6b9..15d67fdfb 100644 --- a/pym/portage/util.py +++ b/pym/portage/util.py @@ -911,7 +911,7 @@ class atomic_ofstream(ObjectProxy): tmp_name = "%s.%i" % (canonical_path, os.getpid()) try: object.__setattr__(self, '_file', - open_func(tmp_name, mode=mode, **kargs)) + open_func(_unicode_encode(tmp_name), mode=mode, **kargs)) return except IOError, e: if canonical_path == filename: @@ -923,7 +923,7 @@ class atomic_ofstream(ObjectProxy): object.__setattr__(self, '_real_name', filename) tmp_name = "%s.%i" % (filename, os.getpid()) object.__setattr__(self, '_file', - open_func(tmp_name, mode=mode, **kargs)) + open_func(_unicode_encode(tmp_name), mode=mode, **kargs)) def _get_target(self): return object.__getattribute__(self, '_file') diff --git a/pym/repoman/checks.py b/pym/repoman/checks.py index 4c2bcd113..76425a817 100644 --- a/pym/repoman/checks.py +++ b/pym/repoman/checks.py @@ -516,7 +516,7 @@ def run_checks(contents, pkg): if here_doc_delim is None: here_doc = _here_doc_re.match(line) if here_doc is not None: - here_doc_delim = re.compile('^%s$' % here_doc.group(1)) + here_doc_delim = re.compile(r'^\s*%s$' % here_doc.group(1)) if here_doc_delim is None: # We're not in a here-document. -- 2.26.2