def check_pyprc(self): output.part_title('Checking .pypirc for egg-release targets') pypirc_path = os.path.expanduser('~/.pypirc') if not os.path.isfile(pypirc_path): # ~/.pypirc required output.error('Could not find the file %s' % pypirc_path, exit=True) config = ConfigParser.ConfigParser() config.readfp(open(pypirc_path)) indexservers = config.get('distutils', 'index-servers').strip().split('\n') sections = [] basic_namespace = scm.get_package_name('.').split('.')[0] for srv in indexservers: # test if its properly configured if config.has_section(srv): print '* found target "%s"' % output.colorize(srv, output.WARNING) sections.append(srv) if basic_namespace in sections: self.pypi_target = basic_namespace else: self.pypi_target = '' msg = 'Please specify a pypi target for the egg relase [%s]' % \ output.colorize(self.pypi_target, output.BOLD_WARNING) pypi_target_input = input.prompt(msg, lambda v:\ (self.pypi_target and not v) or v in sections and True or 'Please select a target listed above') if pypi_target_input: self.pypi_target = pypi_target_input
def check_versions(self): output.part_title('Checking package versions') version_file = os.path.join(scm.get_package_name('.').replace('.', '/'), 'version.txt') trunk_version = open(version_file).read().strip() print ' * Current version of trunk: %s' %\ output.colorize(trunk_version, output.WARNING) next_version = trunk_version.split('-')[0] existing_tags = scm.get_existing_tags('.') if next_version in existing_tags.keys(): output.warning('Tag %s already existing' % next_version) # ask for next tag version prompt_msg = 'Which version do you want to release now? [%s]' % \ output.colorize(next_version, output.BOLD_WARNING) next_version_input = input.prompt(prompt_msg, lambda v:v in existing_tags.keys() and 'Tag already existing' or True) if next_version_input: next_version = next_version_input # ask for next trunk version next_trunk_version = next_version + '-dev' next_trunk_version = self.bump_version_proposal(next_trunk_version) prompt_msg = 'Which version should trunk have afterwards? [%s]' % \ output.colorize(next_trunk_version, output.BOLD_WARNING) next_trunk_version_input = input.prompt(prompt_msg) if next_trunk_version_input: next_trunk_version = next_trunk_version_input print ' * The version of the tag will be: %s' %\ output.colorize(next_version, output.WARNING) print ' * New version of the trunk will be: %s' %\ output.colorize(next_trunk_version, output.WARNING) self.new_tag_version = next_version self.new_trunk_version = next_trunk_version
def show_description(self, rows=None, higlight=None): description = self.get_description() if not rows: rows = range(len(description)) for i in rows: p = '%i:' % (i + 1) + ' ' + description[i] if i == higlight: print output.colorize(p, output.WARNING) else: print p
def notify(self, state, problem='', solution='', problem_level=0, pause=True): """Notify the user of a problem """ if state: print ' OK' else: prob_type, prob_color = self.PROBLEM_LEVELS[problem_level] print ' ', output.colorize(prob_type, prob_color), \ output.colorize(problem, prob_color) if solution: print ' SOLUTION:', solution if pause: input.prompt('[ENTER TO CONTINUE]') print ''
def __call__(self): scm.tested_for_scms(('svn', 'gitsvn'), '.') svn_url = scm.get_svn_url('.').split('/') svn_root_url = scm.get_package_root_url('.').split('/') package_name = scm.get_package_name('.') path = os.path.abspath(os.path.join( (len(svn_url) - len(svn_root_url) - 1) * '../', package_name.replace('.', '/'), 'version.txt', )) if not os.path.isfile(path): output.error('Could not find file %s' % path, exit=1) version = open(path).read().strip() print ' Version of %s: %s' % ( output.colorize(package_name, output.WARNING), output.colorize(version, output.WARNING), )
def __call__(self): pinnings = self._get_pinnings_by_package() def _format_line(pkg, extra, version, file, current=False): pkgname = pkg if extra: pkgname += '[%s]' % extra if current: indent = ' ' * 4 + '*' else: indent = ' ' * 5 txt = ' '.join((indent, pkgname, '=', version, '@', file)) if current: return output.colorize(txt, output.WARNING) else: return txt first = True for pkg, dep_extra, dep_version in self._get_packages(): current_version = None if first: first = False else: print '' print '' print output.colorize(pkg, output.INFO) if dep_version: current_version = None print _format_line(pkg, dep_extra, dep_version, './setup.py') if pkg.lower() in pinnings.keys(): pkg_pinnings = pinnings[pkg.lower()][:] pkg_pinnings.reverse() for file, extra, version in pkg_pinnings: if not current_version: current_version = version print _format_line(pkg, extra, version, file, current=True) else: print _format_line(pkg, extra, version, file) if self.options.new: new_dists = self._get_newer_versions_for(pkg, current_version) if len(new_dists) > 0: print output.colorize('newer distributions than %s:' % current_version, output.ERROR) for dist in new_dists: print ' ' * 5, output.colorize(dist, output.ERROR)
def _add_rows(dependencies, indent=0): for package, extra, v in dependencies: color = None ctag = package in versions.keys() and \ str(versions[package]) or '' info = scm.PackageInfoMemory().get_info( package, force_reload=force_reload, prompt=(not self.options.quiet)) maintainer = scm.PackageInfoMemory().get_maintainer_for( package, with_extra=extra) or '' ntag = info and str(info['newest_tag']) or '' if ntag and ctag: if ntag < ctag: ntag = output.colorize(ntag, output.WARNING) color = output.WARNING elif ntag > ctag: ntag = output.colorize(ntag, output.ERROR) color = output.ERROR pinnings[package] = ntag elif ntag == ctag: ntag = output.colorize(ntag, output.INFO) color = output.INFO elif ntag: pinnings[package] = ntag chg = '' if info and info['changes']: chg = output.colorize('YES', output.WARNING) color = output.WARNING name = extra and '%s[%s]' % (package, extra) or package name = ' ' * indent + name table.push(( color and output.colorize(name, color) or name, ctag, ntag, chg, maintainer, )) if indent < limit: sub_deps = scm.PackageInfoMemory().get_dependencies_for( package, with_extra=extra) if sub_deps: _add_rows(sub_deps, indent + 1)
def _format_line(pkg, extra, version, file, current=False): pkgname = pkg if extra: pkgname += '[%s]' % extra if current: indent = ' ' * 4 + '*' else: indent = ' ' * 5 txt = ' '.join((indent, pkgname, '=', version, '@', file)) if current: return output.colorize(txt, output.WARNING) else: return txt
def __call__(self): if not os.path.isfile('setup.py'): output.error('File not found: %s' % os.path.abspath('setup.py'), exit=1) cmd = '%s setup.py check --restructuredtext --strict' % sys.executable if self.options.show: self.show_description() return elif self.options.browser: description = '\n'.join(self.get_description()) response = open('long_description.html', 'w+') publish_file(writer_name='html', destination=response, source=StringIO(description)) runcmd_with_exitcode('open long_description.html') return code, response, error = runcmd_with_exitcode(cmd, log=True, respond=True, respond_error=True) if code == 0: print '* docs okay' else: xpr = re.compile('\(line ([\d]*)\)') description = self.get_description() error = error.strip().split('\n') for err in error: print output.colorize(err, output.ERROR) line = xpr.search(err) if line: line_nr = int(line.groups()[0]) - 1 start_line = line_nr - self.options.offrows start_line = start_line > 0 and start_line or 0 end_line = line_nr + self.options.offrows + 1 end_line = end_line < len(description) and \ end_line or len(description) line_range = range(start_line, end_line) self.show_description(line_range, higlight=line_nr) print ''
def get_svn_url(self, package_name): # what's the svn url? svn_url = None namespace = package_name.split('.')[0] # are there already packages checked out with same namespace? dirs = [os.path.abspath(d) for d in os.listdir('.')] dirs += [os.path.join(git.get_gitsvn_cache_path(), d) for d in os.listdir(git.get_gitsvn_cache_path())] for path in dirs: dir = os.path.basename(path) if dir.startswith('%s.' % namespace): try: tmp_url = '/'.join( scm.get_package_root_url( path).split('/')[:-1]) if tmp_url and \ '%s/' % package_name in svn.listdir(tmp_url): svn_url = os.path.join(tmp_url, package_name, 'trunk') break except scm.NotAScm: pass if svn_url: print ' * found a package under %s' % svn_url msg = 'SVN project trunk url [%s]' % \ output.colorize(svn_url, output.BOLD_WARNING) def input_validator(v): if not v: return True if not svn.isdir(v.strip()): return 'URL not found' return True url_input = input.prompt(msg, input_validator) if url_input: svn_url = url_input.strip() else: msg = 'SVN project trunk url:' def input_validator2(v): if not v or not svn.isdir(v.strip()): return 'URL not found' return True url_input = input.prompt(msg, input_validator2) svn_url = url_input.strip() # check svn layout, give the user a chance to create the dirs svn.check_project_layout(svn_url, raise_exception=False) return svn_url
def pre_build_check(self): """ Check if a build will work later. Check this before doing anything by building and loading the egg. """ output.part_title('Make a test-build for preventing bad dists') cwd = os.getcwd() # make a sdist runcmd('%s setup.py sdist' % sys.executable) os.chdir('dist') # switch dir print output.colorize('cd dist', output.INFO) # extract runcmd('tar -xf *.tar.gz') # find extracted dir / chdir distdir = None for file_ in os.listdir('.'): if os.path.isdir(file_): distdir = file_ break if not distdir: output.error('Something is wrong: could not find extracted dist directory', exit=1) os.chdir(distdir) print output.colorize('cd %s' % distdir, output.INFO) # test setup.py cmd = '%s setup.py egg_info' % sys.executable state, response, error = runcmd_with_exitcode(cmd, respond=True, respond_error=True) # cd back to original dir os.chdir(cwd) # remove stuff runcmd('rm -rf dist') # did it work? if state != 0: output.error('Something\'s wrong: could not load setup.py on distribution, ' +\ 'you may have a problem with your setup.py / MANIFEST.in:', exit=(not error and True or False)) if response: print output.colorize(response, output.INFO) if error: output.error(error, exit=True) # check locales locales_dir = os.path.join(scm.get_package_name('.').replace('.', '/'), 'locales') if os.path.isdir(locales_dir): for basedir, dirs, files in os.walk(locales_dir): for file_ in files: path = os.path.join(basedir, file_) if path.endswith('.po'): # check with msgfmt exitcode, errors = runcmd_with_exitcode( 'msgfmt -o /dev/null %s' % path, log=True, respond_error=True) if exitcode > 0: output.error(errors, exit=True) data = open(path).read() if 'fuzzy' in data: print path output.error('You have "Fuzzy" entries in your ' 'translations! I\'m not releasing ' 'it like this.', exit=True)
def check_manifest(self): """ Checks the MANIFEST.in file and gives advices. It returns if the release action can be continued """ if self.options.release_egg_only: return True namespace = scm.get_package_name('.').split('.')[0] required_lines = ( 'recursive-include %s *' % namespace, 'recursive-include docs *', 'include *.txt', 'global-exclude *.pyc', 'global-exclude ._*', ) unused_lines = ( 'include setup.py', 'include README.txt', 'include CONTRIBUTORS.txt', 'global-exclude *pyc', 'global-exclude *mo', 'global-exclude *.mo', ) created = False modified = False commit_message = '' if not os.path.isfile('MANIFEST.in'): output.warning('Could not find the file ./MANIFEST.in, creating one') f = open('MANIFEST.in', 'w') f.write('\n'.join(required_lines)) f.close() print 'created MANIFEST.in with following content:' print output.colorize(open('MANIFEST.in').read(), output.INFO) print '' commit_message = 'added MANIFEST.in for %s' % scm.get_package_name('.') created = True else: # check the existing file current_lines = [x.strip() for x in open('MANIFEST.in').readlines()] missing_lines = [x for x in required_lines if x.strip() not in current_lines] files_to_remove = [x for x in unused_lines if x.strip() in current_lines] if len(missing_lines) > 0 or len(files_to_remove) > 0: new_lines = current_lines if len(missing_lines) > 0: output.warning('./MANIFEST.in: added some required lines:') print output.colorize('\n'.join(missing_lines), output.INFO) print '' new_lines += missing_lines if len(files_to_remove) > 0: output.warning('./MANIFEST.in: removed some unused lines:') print output.colorize('\n'.join(files_to_remove), output.ERROR) print '' new_lines = filter(lambda x:x.strip() not in files_to_remove, new_lines) f = open('MANIFEST.in', 'w') f.write('\n'.join(new_lines)) f.close() commit_message = 'updated MANIFEST.in for %s' % scm.get_package_name('.') modified = True if created or modified: # commit it ? if input.prompt_bool('Would you like to commit the MANIFEST.in?'): if created: scm.add_and_commit_files(commit_message, 'MANIFEST.in') return True elif modified: scm.commit_files(commit_message, 'MANIFEST.in') return True return False else: return True
def notify_fix_completed(self): print output.colorize('Fix completed successfully', output.INFO) print ''
def notify_check(self, title): """Print check-title to the stdout """ print output.colorize('CHECK:', output.BOLD_INFO), \ output.colorize(title, output.INFO)
def check_requires(self): """ Checks, if there are missing dependencies """ self.notify_part('Check dependencies') # get current requires requires = self.egginfo.install_requires # extend it with extra requires if self.egginfo.extras_require: for ename, erequires in self.egginfo.extras_require.items(): requires.extend(erequires) print ' current requirements (including extras):' for egg in requires: print ' -', egg print '' # add the requirements without extras too for egg in requires[:]: if '[' in egg: requires.append(egg.split('[')[0]) self.notify_check('Its not necessary to import some default plone / zope stuff') failures = False for egg in requires: if egg in PACKAGE_REQUIREMENTS_INADVISABLE and egg not in TESTING_PACKAGES: self.notify(False, 'Maybe you should remove the requirement ' +\ output.colorize(egg, output.ERROR) +\ output.colorize('. It seems to be in a python, ' +\ 'zope or plone distribution and ' +\ 'those packages should not be ' +\ 'set as requirement.', output.WARNING), problem_level=2) failures = True if not failures: self.notify(True) self.notify_check('Check imports on python files and zcml stuff') propose_requires = [] # contains ipath:file mapping of python and zcml imports ipath_file_mapping = {} # SEARCH PYTHON FILES # make a grep on python files py_grep_results = runcmd("find . -name '*.py' -exec grep -Hr 'import ' {} \;", respond=True) # cleanup: # - strip rows # - remove the rows with spaces (they are not realle imports) # - remove "as xxx" py_grep_results = filter(lambda row: ' ' in row, [row.strip().split(' as ')[0] for row in py_grep_results]) for row in py_grep_results: file_, statement = row.split(':') # make a import path ipath = statement.replace('from ', '').replace(' import ', '.').replace('import', '').strip() ipath = ipath.replace('...', '').replace('>>>', '') ipath_parts = ipath.split('.') if '#' in ipath: continue # ignore namespace imports (python internals etc) if len(ipath_parts) == 1: continue ipath_file_mapping[ipath] = file_ # SEARCH ZCML FILES cmd = "find . -name '*.zcml' -exec grep -Hr '\(layer\|package\|for\)=' {} \;" zcml_grep_results = runcmd(cmd, respond=True) # cleanup results zcml_xpr = re.compile('(for|layer)="(.*?)("|$)') for row in zcml_grep_results: file_, stmt = row.split(':', 1) stmt = stmt.strip() match = zcml_xpr.search(stmt) if not match: # maybe we have a more complicated statement (e.g. multiline) break ipath = match.groups()[1].strip() ipath = ipath.replace('*', '').strip() if '#' in ipath: continue if not ipath.startswith('.'): ipath_file_mapping[ipath] = file_ # for later use guessed_related_packages = self._get_guessed_related_packages() # HANDLE ALL IMPORTS for ipath, file_ in ipath_file_mapping.items(): ipath_parts = ipath.split('.') # ignore local imports if ipath.startswith(scm.get_package_name('.')): continue # is it already required? found = False for egg in requires: if ipath.startswith(egg): found = True break if not found: # is it already proposed? for egg in propose_requires: if ipath.startswith(egg): found = True break if not found: # is it ignored? for egg in PACKAGE_REQUIREMENTS_INADVISABLE + TESTING_PACKAGES: if ipath.startswith(egg): found = True break if found: continue # maybe we have a module which import relatively module_path = os.path.join(os.path.dirname(file_), ipath_parts[0]) if os.path.isfile(module_path + '.py') or \ os.path.isfile(module_path + '/__init__.py'): continue # start on level 2 and for searching egg guessed_egg_names = ['.'.join(ipath_parts[:i]) for i, part in enumerate(ipath_parts) if i > 1] # does one of the eggs exist? found = False # is there package in our src directory, if we are in one? for egg_name in guessed_egg_names: if egg_name.strip() in guessed_related_packages: if egg_name.strip() not in propose_requires: propose_requires.append(egg_name.strip()) found = True break # .. in pypi if not found: for egg_name in guessed_egg_names: if len(self.find_egg_in_index(egg_name)) > 0: if egg_name.strip() not in propose_requires: propose_requires.append(egg_name.strip()) found = True break # .. or do we have one in the svn cache? if not found: for egg_name in guessed_egg_names: if scm.guess_package_url(egg_name): if egg_name.strip() not in propose_requires: propose_requires.append(egg_name.strip()) found = True break if not found: print ' ', output.colorize(ipath, output.INFO), 'in', \ output.colorize(file_, output.INFO), \ output.colorize('is not covered by requirements ' 'and I could find a egg with such a name', output.WARNING) if scm.get_package_name('.') in propose_requires: propose_requires.remove(scm.get_package_name('.')) if len(propose_requires)==0: self.notify(True) return propose_requires.sort() print '' print ' There are some requirements missing. I propose to add these:' for egg in propose_requires: print ' ', egg print '' # try to add them automatically: if input.prompt_bool('Should I try to add them?'): rows = open('setup.py').read().split('\n') # find "install_requires" frows = filter(lambda row:row.strip().startswith('install_requires='), rows) if len(frows) != 1: output.error('Somethings wrong with your setup.py: expected only ' 'one row containing "install_requires=", but ' 'got %i' % len(frows), exit=1) insert_at = rows.index(frows[0]) + 1 for egg in propose_requires: rows.insert(insert_at, ' ' * 8 + "'%s'," % egg) file_ = open('setup.py', 'w') file_.write('\n'.join(rows)) file_.close() self._validate_setup_py() scm.add_and_commit_files('setup.py: added missing dependencies', 'setup.py') self.notify_fix_completed()
def guess_url(self, package, prompt=False, required=False): """ Tries to guess the url of a package. If it's not able to guess, it asks or returns None, dependening on *prompt* """ # first, we check if we already know the name if self.has_package(package): return self.get(package) # we will search some places, make a list of possible hint-directories hint_dirs = [] # the parent directory may contain the package or other packages with the same # namespace (e.g. src-directory) hint_dirs.append(os.path.abspath("..")) # the gitsvn-cache directory contains several packages, the required package or # a package with the same namespace? hint_dirs.append(os.path.abspath(git.get_gitsvn_cache_path())) # now we try to find the package in the hint_dirs for dir in hint_dirs: path = os.path.join(dir, package) if os.path.isdir(path) and is_scm(path): try: url = get_package_root_url(path) except NotAScm: pass else: self.set(package, url) return url # now lets guess it with the namespace... # first we check the packages we alreay know the path: namespace = package.split(".")[0] for pkg, url in self._packages.items(): if pkg.startswith("%s." % namespace): try: dir_url = "/".join(url.split("/")[:-1]) if package + "/" in svn.listdir(dir_url): url = os.path.join(dir_url, package) self.set(package, url) return url except NotAScm: pass # could not find any? we may need to ask the user... if prompt: def input_validator(v): if not required and not v: return True if not v or not svn.isdir(v.strip()): return "Invalid SVN-URL" return True colorized_package = output.colorize(package, output.BOLD_WARNING) msg = output.colorize("I'm having trouble to guess the SVN-URL for the package", output.WARNING) msg += " " + colorized_package print msg msg = "SVN-URL of %s" % colorized_package if not required: msg += output.colorize(" or nothing", output.WARNING) url_input = input.prompt(msg, input_validator) if url_input: url = svn.get_package_root_url(url_input) self.set(package, url) return url return None