Control: tags 936346 + patch Control: tags 936346 + pending Dear maintainer,
I've prepared an NMU for crudini (versioned as 0.9.3-0.1) and uploaded it to DELAYED/15. Please feel free to tell me if I should cancel it. cu Adrian
diff -Nru crudini-0.7/crudini crudini-0.9.3/crudini --- crudini-0.7/crudini 2015-06-14 03:27:16.000000000 +0300 +++ crudini-0.9.3/crudini 2019-08-30 14:26:49.000000000 +0300 @@ -1,16 +1,16 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- # vim:fileencoding=utf8 # -# Copyright (C) 2013-2015, Pádraig Brady <p...@draigbrady.com> +# Copyright © Pádraig Brady <p...@draigbrady.com> # # This program is free software; you can redistribute it and/or modify it # under the terms of the GPLv2, the GNU General Public License version 2, as # published by the Free Software Foundation. http://gnu.org/licenses/gpl.html +from __future__ import print_function import atexit -from cStringIO import StringIO -import ConfigParser +import sys import contextlib import errno import getopt @@ -20,13 +20,19 @@ import pipes import shutil import string -import sys import tempfile +if sys.version_info[0] >= 3: + from io import StringIO + import configparser +else: + from cStringIO import StringIO + import ConfigParser as configparser + def error(message=None): if message: - sys.stderr.write(message+'\n') + sys.stderr.write(message + '\n') def delete_if_exists(path): @@ -36,15 +42,33 @@ os.unlink(path) except EnvironmentError as e: if e.errno != errno.ENOENT: - print str(e) + print(str(e)) raise +# TODO: support configurable options for various ini variants. +# For now just support parameters without '=' specified +class CrudiniInputFilter(): + def __init__(self, fp): + self.fp = fp + self.crudini_no_arg = False + + def readline(self): + line = self.fp.readline() + # XXX: This doesn't handle ;inline comments. + # Really should be done within inparse. + if (line and line[0] not in '[ \t#;\n\r' and + '=' not in line and ':' not in line): + self.crudini_no_arg = True + line = line[:-1] + ' = crudini_no_arg\n' + return line + + # XXX: should be done in iniparse. Used to # add support for ini files without a section -class AddDefaultSection(): +class AddDefaultSection(CrudiniInputFilter): def __init__(self, fp): - self.fp = fp + CrudiniInputFilter.__init__(self, fp) self.first = True def readline(self): @@ -52,7 +76,7 @@ self.first = False return '[%s]' % iniparse.DEFAULTSECT else: - return self.fp.readline() + return CrudiniInputFilter.readline(self) class FileLock(object): @@ -104,7 +128,6 @@ - File must be writeable - File should be generally non readable to avoid read lock DoS. Caveats in replace mode: - - Possibility of stale lock files left on crash leading to deadlock. - Less responsive when there is contention.""" def __init__(self, filename, operation, inplace, create): @@ -180,6 +203,9 @@ class CrudiniConfigParser(iniparse.RawConfigParser): def __init__(self, preserve_case=False): iniparse.RawConfigParser.__init__(self) + # Without the following we can't have params starting with "rem"! + # We ignore lines starting with '%' which mercurial uses to include + iniparse.change_comment_syntax('%;#', allow_rem=False) if preserve_case: self.optionxform = str @@ -193,7 +219,7 @@ :param section: str """ - print section + print(section) def name_value(self, name, value, section=None): """Print parameter. @@ -203,17 +229,21 @@ :param section: str (default 'None') """ - print name or value + if value == 'crudini_no_arg': + value = '' + print(name or value) class PrintIni(Print): """Use for ini output format.""" def section_header(self, section): - print "[%s]" % section + print("[%s]" % section) def name_value(self, name, value, section=None): - print name, '=', value.replace('\n', '\n ') + if value == 'crudini_no_arg': + value = '' + print(name, '=', value.replace('\n', '\n ')) class PrintLines(Print): @@ -228,9 +258,11 @@ line += ' ' if name: line += '%s' % name + if value == 'crudini_no_arg': + value = '' if value: line += ' = %s' % value.replace('\n', '\\n') - print line + print(line) class PrintSh(Print): @@ -261,6 +293,8 @@ if not PrintSh._valid_sh_identifier(name): error('Invalid sh identifier: %s' % name) sys.exit(1) + if value == 'crudini_no_arg': + value = '' sys.stdout.write("%s=%s\n" % (name, pipes.quote(value))) @@ -293,7 +327,7 @@ except Exception: t, v, tb = sys.exc_info() delete_if_exists(path) - raise t, v, tb + raise t(v).with_traceback(tb) @staticmethod def file_replace(name, data): @@ -315,7 +349,7 @@ To avoid the above caveats see the --inplace option. """ - (f, tmp) = tempfile.mkstemp(".tmp", prefix=name+".", dir=".") + (f, tmp) = tempfile.mkstemp(".tmp", prefix=name + ".", dir=".") with Crudini.remove_file_on_error(tmp): shutil.copystat(name, tmp) @@ -324,8 +358,17 @@ st = os.stat(name) os.fchown(f, st.st_uid, st.st_gid) - os.write(f, data) - os.fsync(f) # See http://stackoverflow.com/q/7433057/4421 + if sys.version_info[0] >= 3: + os.write(f, bytearray(data, 'utf-8')) + else: + os.write(f, data) + # We assume the existing file is persisted, + # so sync here to ensure new data is persisted + # before referencing it. Otherwise the metadata could + # be written first, referencing the new data, which + # would be nothing if a crash occured before the + # data was allocated/persisted. + os.fsync(f) os.close(f) if hasattr(os, 'replace'): # >= python 3.3 @@ -333,12 +376,16 @@ elif os.name == 'posix': os.rename(tmp, name) # atomic on POSIX else: - backup = tmp+'.backup' + backup = tmp + '.backup' os.rename(name, backup) os.rename(tmp, name) delete_if_exists(backup) - # Sync out the new directory entry also + # Sync out the new directory entry to provide + # better durability that the new inode is referenced + # rather than continuing to reference the old inode. + # This also provides verification in exit status that + # this update completes. O_DIRECTORY = os.O_DIRECTORY if hasattr(os, 'O_DIRECTORY') else 0 dirfd = os.open(os.path.dirname(name) or '.', O_DIRECTORY) os.fsync(dirfd) @@ -411,7 +458,7 @@ --existing[=WHAT] For --set, --del and --merge, fail if item is missing, where WHAT is 'file', 'section', or 'param', or if - not specified; all specifed items. + not specified; all specified items. --format=FMT For --get, select the output FMT. Formats are sh,ini,lines --inplace Lock and write files in place. @@ -421,6 +468,8 @@ --list-sep=STR Delimit list values with \"STR\" instead of \" ,\" --output=FILE Write output to FILE instead. '-' means stdout --verbose Indicate on stderr if changes were made + --help Write this help to stdout + --version Write version to stdout """ % (cmd, cmd, cmd, cmd) ) sys.exit(exitval) @@ -452,7 +501,7 @@ 'version' ] opts, args = getopt.getopt(sys.argv[1:], '', long_options) - except getopt.GetoptError, e: + except getopt.GetoptError as e: error(str(e)) self.usage(1) @@ -460,7 +509,7 @@ if o in ('--help',): self.usage(0) elif o in ('--version',): - print 'crudini 0.7' + print('crudini 0.9.3') sys.exit(0) elif o in ('--verbose',): self.verbose = True @@ -548,8 +597,11 @@ return False def _chksum(self, data): - h = hashlib.md5() - h.update(data) + h = hashlib.sha256() + if sys.version_info[0] >= 3: + h.update(bytearray(data, 'utf-8')) + else: + h.update(data) return h.digest() def _parse_file(self, filename, add_default=False, preserve_case=False): @@ -576,9 +628,12 @@ fp = StringIO(self.data) if add_default: fp = AddDefaultSection(fp) + else: + fp = CrudiniInputFilter(fp) conf = CrudiniConfigParser(preserve_case=preserve_case) conf.readfp(fp) + self.crudini_no_arg = fp.crudini_no_arg return conf except EnvironmentError as e: error(str(e)) @@ -608,14 +663,14 @@ ) self.added_default_section = True - except ConfigParser.MissingSectionHeaderError: + except configparser.MissingSectionHeaderError: conf = self._parse_file( filename, add_default=True, preserve_case=preserve_case ) self.added_default_section = True - except ConfigParser.ParsingError as e: + except configparser.ParsingError as e: error(str(e)) sys.exit(1) @@ -631,14 +686,14 @@ section == iniparse.DEFAULTSECT or self.conf.has_section(section) ): - raise ConfigParser.NoSectionError(section) + raise configparser.NoSectionError(section) else: try: curr_val = self.conf.get(section, param) - except ConfigParser.NoSectionError: + except configparser.NoSectionError: if self.update == 'section': raise - except ConfigParser.NoOptionError: + except configparser.NoOptionError: if self.update == 'param': raise elif (section != iniparse.DEFAULTSECT and @@ -656,11 +711,11 @@ if self.update not in ('param', 'section'): try: curr_val = self.conf.get(section, param) - except ConfigParser.NoOptionError: + except configparser.NoOptionError: if self.mode == "--del": return if value is None: - value = '' + value = 'crudini_no_arg' if self.crudini_no_arg else '' if self.vlist: value = self.update_list( curr_val, @@ -689,11 +744,11 @@ # XXX: Note this doesn't update an item in section # if matching value also in default (global) section. if defaults_to_strip.get(item[0]) != item[1]: - ignore_errs = (ConfigParser.NoOptionError,) + ignore_errs = (configparser.NoOptionError,) if self.section is not None: msection = self.section elif self.update not in ('param', 'section'): - ignore_errs += (ConfigParser.NoSectionError,) + ignore_errs += (configparser.NoSectionError,) try: set_param = True self.set_name_value(msection, item[0], item[1]) @@ -713,13 +768,13 @@ else: if not self.conf.remove_section(self.section) \ and self.update in ('param', 'section'): - raise ConfigParser.NoSectionError(self.section) + raise configparser.NoSectionError(self.section) elif self.value is None: try: if not self.conf.remove_option(self.section, self.param) \ and self.update == 'param': - raise ConfigParser.NoOptionError(self.section, self.param) - except ConfigParser.NoSectionError: + raise configparser.NoOptionError(self.section, self.param) + except configparser.NoSectionError: if self.update in ('param', 'section'): raise else: # remove item from list @@ -803,21 +858,25 @@ self.madded_default_section = self.added_default_section try: - self.conf = self.parse_file(self.cfgfile) - except ConfigParser.ParsingError as e: - error('Error parsing %s: %s' % (self.cfgfile, e.message)) - sys.exit(1) + if self.mode == '--get' and self.param is None: + # Maintain case when outputting params. + # Note sections are handled case sensitively + # even if optionxform is not set. + preserve_case = True + else: + preserve_case = False + self.conf = self.parse_file(self.cfgfile, + preserve_case=preserve_case) - # Take the [DEFAULT] header from the input if present - if ( - self.mode == '--merge' and - self.update not in ('param', 'section') and - not self.madded_default_section and - self.mconf.items(iniparse.DEFAULTSECT) - ): - self.added_default_section = self.madded_default_section + # Take the [DEFAULT] header from the input if present + if ( + self.mode == '--merge' and + self.update not in ('param', 'section') and + not self.madded_default_section and + self.mconf.items(iniparse.DEFAULTSECT) + ): + self.added_default_section = self.madded_default_section - try: if self.mode == '--set': self.command_set() elif self.mode == '--merge': @@ -826,45 +885,46 @@ self.command_del() elif self.mode == '--get': self.command_get() - except ConfigParser.NoSectionError as e: - error('Section not found: %s' % e.section) - sys.exit(1) - except ConfigParser.NoOptionError: - error('Parameter not found: %s' % self.param) - sys.exit(1) - - if self.mode != '--get': - # XXX: Ideally we should just do conf.write(f) here, - # but to avoid iniparse issues, we massage the data a little here - str_data = str(self.conf.data) - if len(str_data) and str_data[-1] != '\n': - str_data += '\n' - if ( - ( - self.added_default_section and - not ( - self.section_explicit_default and - self.mode in ('--set', '--merge') + if self.mode != '--get': + # XXX: Ideally we should just do conf.write(f) here, but to + # avoid iniparse issues, we massage the data a little here + str_data = str(self.conf.data) + if len(str_data) and str_data[-1] != '\n': + str_data += '\n' + + if ( + ( + self.added_default_section and + not ( + self.section_explicit_default and + self.mode in ('--set', '--merge') + ) + ) or + ( + self.mode == '--del' and + self.section == iniparse.DEFAULTSECT and + self.param is None ) - ) or - ( - self.mode == '--del' and - self.section == iniparse.DEFAULTSECT and - self.param is None - ) - ): - # See note at add_section() call above detailing - # where this extra \n comes from that we handle - # here for the edge case of new files. - default_sect = '[%s]\n' % iniparse.DEFAULTSECT - if not self.newline_at_start and \ - str_data.startswith(default_sect + '\n'): - str_data = str_data[len(default_sect) + 1:] - else: - str_data = str_data.replace(default_sect, '', 1) + ): + # See note at add_section() call above detailing + # where this extra \n comes from that we handle + # here for the edge case of new files. + default_sect = '[%s]\n' % iniparse.DEFAULTSECT + if not self.newline_at_start and \ + str_data.startswith(default_sect + '\n'): + str_data = str_data[len(default_sect) + 1:] + else: + str_data = str_data.replace(default_sect, '', 1) + + if self.crudini_no_arg: + # This is the main case + str_data = str_data.replace(' = crudini_no_arg', '') + # Handle setting empty values for existing param= format + str_data = str_data.replace('=crudini_no_arg', '=') + # Handle setting empty values for existing colon: format + str_data = str_data.replace(':crudini_no_arg', ':') - try: changed = self.chksum != self._chksum(str_data) if self.output == '-': @@ -873,26 +933,47 @@ if self.inplace: self.file_rewrite(self.output, str_data) else: - self.file_replace(self.output, str_data) + self.file_replace(os.path.realpath(self.output), + str_data) if self.verbose: def quote_val(val): return pipes.quote(val).replace('\n', '\\n') what = ' '.join(map(quote_val, - filter(bool, - [self.mode, self.cfgfile, - self.section, self.param, - self.value]))) + list(filter(bool, + [self.mode, self.cfgfile, + self.section, self.param, + self.value])))) sys.stderr.write('%s: %s\n' % (('unchanged', 'changed')[changed], what)) - except EnvironmentError as e: + + # Finish writing now to consistently handle errors here + # (and while excepthook is set) + sys.stdout.flush() + except configparser.ParsingError as e: + error('Error parsing %s: %s' % (self.cfgfile, e.message)) + sys.exit(1) + except configparser.NoSectionError as e: + error('Section not found: %s' % e.section) + sys.exit(1) + except configparser.NoOptionError: + error('Parameter not found: %s' % self.param) + sys.exit(1) + except EnvironmentError as e: + # Handle EPIPE as python 2 doesn't catch SIGPIPE + if e.errno != errno.EPIPE: error(str(e)) sys.exit(1) + # Python3 fix for exception on exit: + # https://docs.python.org/3/library/signal.html#note-on-sigpipe + nullf = os.open(os.devnull, os.O_WRONLY) + os.dup2(nullf, sys.stdout.fileno()) def main(): crudini = Crudini() return crudini.run() + if __name__ == "__main__": sys.exit(main()) diff -Nru crudini-0.7/crudini.1 crudini-0.9.3/crudini.1 --- crudini-0.7/crudini.1 2015-06-14 03:31:34.000000000 +0300 +++ crudini-0.9.3/crudini.1 2019-08-30 14:33:19.000000000 +0300 @@ -1,5 +1,5 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.6. -.TH CRUDINI "1" "June 2015" "crudini 0.7" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. +.TH CRUDINI "1" "August 2019" "crudini 0.9.3" "User Commands" .SH NAME crudini \- manipulate ini files .SH SYNOPSIS @@ -21,7 +21,7 @@ \fB\-\-existing\fR[=\fI\,WHAT\/\fR] For \fB\-\-set\fR, \fB\-\-del\fR and \fB\-\-merge\fR, fail if item is missing, where WHAT is 'file', 'section', or 'param', or if -not specified; all specifed items. +not specified; all specified items. .TP \fB\-\-format\fR=\fI\,FMT\/\fR For \fB\-\-get\fR, select the output FMT. @@ -43,6 +43,12 @@ .TP \fB\-\-verbose\fR Indicate on stderr if changes were made +.TP +\fB\-\-help\fR +Write this help to stdout +.TP +\fB\-\-version\fR +Write version to stdout .SH EXAMPLES # Add/Update a var .IP diff -Nru crudini-0.7/debian/changelog crudini-0.9.3/debian/changelog --- crudini-0.7/debian/changelog 2015-07-12 03:55:06.000000000 +0300 +++ crudini-0.9.3/debian/changelog 2019-12-20 16:37:02.000000000 +0200 @@ -1,3 +1,11 @@ +crudini (0.9.3-0.1) unstable; urgency=medium + + * Non-maintainer upload. + * New upstream release. + * Use Python 3. (Closes: #936346) + + -- Adrian Bunk <b...@debian.org> Fri, 20 Dec 2019 16:37:02 +0200 + crudini (0.7-1) unstable; urgency=medium * New upstream version diff -Nru crudini-0.7/debian/control crudini-0.9.3/debian/control --- crudini-0.7/debian/control 2015-07-12 03:54:48.000000000 +0300 +++ crudini-0.9.3/debian/control 2019-12-20 16:37:02.000000000 +0200 @@ -4,11 +4,11 @@ Maintainer: Python Applications Packaging Team <python-apps-t...@lists.alioth.debian.org> Uploaders: Zev Benjamin <z...@mit.edu>, Luke Faraone <lfara...@debian.org> Build-Depends: debhelper (>= 8.0.0), - python, + python3, dh-python, # Needed to generate manpage help2man, - python-iniparse + python3-iniparse Standards-Version: 3.9.6 Homepage: http://www.pixelbeat.org/programs/crudini/ Vcs-Svn: svn://anonscm.debian.org/python-apps/packages/crudini/trunk/ @@ -16,7 +16,7 @@ Package: crudini Architecture: any -Depends: ${python:Depends}, ${misc:Depends}, python-iniparse +Depends: ${python3:Depends}, ${misc:Depends}, python3-iniparse Description: utility for manipulating ini files crudini is a utility to simplify reading and updating ini files from shell scripts, so named as it provides CRUD functionality. It diff -Nru crudini-0.7/debian/patches/01-typofix.diff crudini-0.9.3/debian/patches/01-typofix.diff --- crudini-0.7/debian/patches/01-typofix.diff 2015-07-12 03:51:33.000000000 +0300 +++ crudini-0.9.3/debian/patches/01-typofix.diff 1970-01-01 02:00:00.000000000 +0200 @@ -1,16 +0,0 @@ -Description: Fix a typo in the documentation. -Author: Luke Faraone <lfara...@debian.org> -Origin: vendor -Forwarded: https://github.com/pixelb/crudini/pull/25 - ---- crudini-0.7.orig/crudini -+++ crudini-0.7/crudini -@@ -411,7 +411,7 @@ Options: - - --existing[=WHAT] For --set, --del and --merge, fail if item is missing, - where WHAT is 'file', 'section', or 'param', or if -- not specified; all specifed items. -+ not specified; all specified items. - --format=FMT For --get, select the output FMT. - Formats are sh,ini,lines - --inplace Lock and write files in place. diff -Nru crudini-0.7/debian/patches/python3.patch crudini-0.9.3/debian/patches/python3.patch --- crudini-0.7/debian/patches/python3.patch 1970-01-01 02:00:00.000000000 +0200 +++ crudini-0.9.3/debian/patches/python3.patch 2019-12-20 16:37:02.000000000 +0200 @@ -0,0 +1,12 @@ +Description: crudini: Use Python 3 instead of Python 2 +Author: Adrian Bunk <b...@debian.org> +Bug-Debian: https://bugs.debian.org/936346 + +--- crudini-0.9.3.orig/crudini ++++ crudini-0.9.3/crudini +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + # -*- coding: utf-8 -*- + # vim:fileencoding=utf8 + # diff -Nru crudini-0.7/debian/patches/series crudini-0.9.3/debian/patches/series --- crudini-0.7/debian/patches/series 2015-07-12 03:50:33.000000000 +0300 +++ crudini-0.9.3/debian/patches/series 2019-12-20 16:37:02.000000000 +0200 @@ -1 +1 @@ -01-typofix.diff +python3.patch diff -Nru crudini-0.7/debian/rules crudini-0.9.3/debian/rules --- crudini-0.7/debian/rules 2013-12-04 20:56:34.000000000 +0200 +++ crudini-0.9.3/debian/rules 2019-12-20 16:37:02.000000000 +0200 @@ -10,4 +10,4 @@ #export DH_VERBOSE=1 %: - dh $@ --with python2 + dh $@ --with python3 diff -Nru crudini-0.7/Makefile crudini-0.9.3/Makefile --- crudini-0.7/Makefile 2015-06-14 03:27:03.000000000 +0300 +++ crudini-0.9.3/Makefile 2019-08-30 14:26:28.000000000 +0300 @@ -1,5 +1,5 @@ name = crudini -version = 0.7 +version = 0.9.3 all: help2man -n "manipulate ini files" -o crudini.1 -N ./crudini-help diff -Nru crudini-0.7/NEWS crudini-0.9.3/NEWS --- crudini-0.7/NEWS 2015-06-14 03:27:37.000000000 +0300 +++ crudini-0.9.3/NEWS 2019-08-30 14:27:03.000000000 +0300 @@ -1,5 +1,49 @@ crudini NEWS -*- outline -*- +* Noteworthy changes in release 0.9.3 (2019-08-30) + +** Bug fixes + + Reading ini files with windows line endings is again supported. + Regression added in v0.9. + +** Improvements + + python 3 support. + + +* Noteworthy changes in release 0.9 (2016-12-13) + +** Bug fixes + + Write errors to stdout are diagnosed correctly and consistently. + + Replacing symlinks now replaces the target rather than the symlink itself. + +** Changes in behavior + + The case of parameters is maintained with --get. + +** Improvements + + Single token parameters (without equals) are now supported, + which are used in mysql config for example. + + +* Noteworthy changes in release 0.8 (2016-11-23) + +** Bug fixes + + crudini now handles parameters starting with "rem". + Previously an entry such as "remote = 1" would be ignored. + +** New features + + Support mercurial config files by treating lines starting + with '%' as comments, thus ignoring mercurial '%include' + and '%unset' directives. + + * Noteworthy changes in release 0.7 (2015-06-14) ** Bug fixes diff -Nru crudini-0.7/noequals.ini crudini-0.9.3/noequals.ini --- crudini-0.7/noequals.ini 1970-01-01 02:00:00.000000000 +0200 +++ crudini-0.9.3/noequals.ini 2016-12-13 15:21:50.000000000 +0200 @@ -0,0 +1,19 @@ +# Differences from mysql.conf +# #comments can't be at middle of line +# single/double quotes are not stripped +# leading spaces on line are not ignored + +!include directives treated as parameters + +[noequals] +param1 +param2= +param3 = +colon1: +colon2 : +space param +never #comment +not ;comment +multiline=val + spaceval + tabval diff -Nru crudini-0.7/README crudini-0.9.3/README --- crudini-0.7/README 2015-06-14 03:31:35.000000000 +0300 +++ crudini-0.9.3/README 2019-08-30 14:33:19.000000000 +0300 @@ -9,7 +9,7 @@ --existing[=WHAT] For --set, --del and --merge, fail if item is missing, where WHAT is 'file', 'section', or 'param', or if - not specified; all specifed items. + not specified; all specified items. --format=FMT For --get, select the output FMT. Formats are sh,ini,lines --inplace Lock and write files in place. @@ -19,6 +19,8 @@ --list-sep=STR Delimit list values with "STR" instead of " ," --output=FILE Write output to FILE instead. '-' means stdout --verbose Indicate on stderr if changes were made + --help Write this help to stdout + --version Write version to stdout Examples: diff -Nru crudini-0.7/setup.py crudini-0.9.3/setup.py --- crudini-0.7/setup.py 2015-06-14 03:26:59.000000000 +0300 +++ crudini-0.9.3/setup.py 2019-08-30 14:26:35.000000000 +0300 @@ -7,9 +7,10 @@ def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() + setup( name="crudini", - version="0.7", + version="0.9.3", author="Pádraig Brady", author_email="p...@draigbrady.com", description=("A utility for manipulating ini files"), @@ -24,6 +25,6 @@ "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "Programming Language :: Python :: 2", ], - install_requires=['iniparse'], + install_requires=['iniparse>=0.3.2'], scripts=["crudini"] ) diff -Nru crudini-0.7/tests/example.lines crudini-0.9.3/tests/example.lines --- crudini-0.7/tests/example.lines 2014-09-05 18:46:40.000000000 +0300 +++ crudini-0.9.3/tests/example.lines 2016-12-10 17:52:57.000000000 +0200 @@ -17,7 +17,7 @@ [ section1 ] empty [ section1 ] python_interpolate = %(dup1)s/blah [ section1 ] interpolate2 = ${dup1}/blah -[ section1 ] caps = not significant +[ section1 ] Caps = not significant [ section1 ] combine = sections [ empty section ] [ non-sh-compat ] space name = val diff -Nru crudini-0.7/tests/section1.ini crudini-0.9.3/tests/section1.ini --- crudini-0.7/tests/section1.ini 2013-01-19 19:00:45.000000000 +0200 +++ crudini-0.9.3/tests/section1.ini 2016-12-10 17:51:43.000000000 +0200 @@ -19,5 +19,5 @@ empty = python_interpolate = %(dup1)s/blah interpolate2 = ${dup1}/blah -caps = not significant +Caps = not significant combine = sections diff -Nru crudini-0.7/tests/section1.lines crudini-0.9.3/tests/section1.lines --- crudini-0.7/tests/section1.lines 2013-05-15 20:19:55.000000000 +0300 +++ crudini-0.9.3/tests/section1.lines 2016-12-10 17:52:30.000000000 +0200 @@ -16,5 +16,5 @@ [ section1 ] empty [ section1 ] python_interpolate = %(dup1)s/blah [ section1 ] interpolate2 = ${dup1}/blah -[ section1 ] caps = not significant +[ section1 ] Caps = not significant [ section1 ] combine = sections diff -Nru crudini-0.7/tests/section1.sh crudini-0.9.3/tests/section1.sh --- crudini-0.7/tests/section1.sh 2013-01-19 19:01:04.000000000 +0200 +++ crudini-0.9.3/tests/section1.sh 2016-12-10 17:52:03.000000000 +0200 @@ -18,5 +18,5 @@ empty='' python_interpolate='%(dup1)s/blah' interpolate2='${dup1}/blah' -caps='not significant' +Caps='not significant' combine=sections diff -Nru crudini-0.7/tests/test.sh crudini-0.9.3/tests/test.sh --- crudini-0.7/tests/test.sh 2015-02-06 20:10:24.000000000 +0200 +++ crudini-0.9.3/tests/test.sh 2019-01-01 17:06:31.000000000 +0200 @@ -1,7 +1,7 @@ #!/bin/bash trap "exit 130" INT -cleanup() { rm -f test.ini good.ini example.ini; exit; } +cleanup() { rm -f err noequals*.ini test.ini ltest.ini good.ini example.ini; exit; } trap cleanup EXIT export PATH=..:$PATH @@ -217,6 +217,13 @@ test "$(crudini --get test.ini)" = 'section1' || fail ok +# Ensure we handle comments correctly +printf '%s\n' '[DEFAULT]' '#c1' ';c2' '%inc1' > test.ini +test "$(crudini --get test.ini)" = '' || fail +printf '%s\n' '[section1]' 'remote=1' > test.ini +test "$(crudini --get test.ini 'section1')" = 'remote' || fail +ok + # missing bits :> test.ini crudini --get missing.ini 2>/dev/null && fail @@ -465,3 +472,34 @@ crudini --set file.conf '' option 2 || fail diff -u good.conf file.conf && ok || fail rm file.conf good.conf + +# ensure errors diagnosed correctly +crudini --get example.ini 2>err | : +! test -s err && ok || fail #EPIPE ignored +if test -e /dev/full; then +crudini --get example.ini 2>err >/dev/full +grep -q 'No space left' err && ok || fail +fi + +# ensure symlinks handled correctly in file replace mode +printf '%s\n' '[section]' 'param = value' > test.ini +ln -s test.ini ltest.ini +crudini --set ltest.ini section param newvalue || fail +test "$(crudini --get test.ini section param)" = 'newvalue' && ok || fail +crudini --output=ltest.ini --set ltest.ini section param newvalue2 || fail +test "$(crudini --get test.ini section param)" = 'newvalue2' && ok || fail + +# Test single token parameters (without equals) +cp ../noequals.ini . +crudini --get noequals.ini >/dev/null && ok || fail +cp noequals.ini noequals_new.ini +printf '%s\n' 'new' 'new_equals = ' >> noequals_new.ini +for param in param{1..3} colon{1..2} new; do + crudini --set noequals.ini noequals $param || fail +done +crudini --set noequals.ini noequals new_equals '' || fail +diff -u noequals.ini noequals_new.ini && ok || fail + +# Test can read windows format files +printf '%s\r\n' '' 'param = value' > test.ini +crudini --get test.ini DEFAULT param > /dev/null || fail diff -Nru crudini-0.7/TODO crudini-0.9.3/TODO --- crudini-0.7/TODO 2015-02-06 15:26:13.000000000 +0200 +++ crudini-0.9.3/TODO 2016-12-13 14:27:00.000000000 +0200 @@ -1,5 +1,3 @@ -support variables without = - support --set,--merge of #commented name=value with operation controlled with --with-comment=always diff -Nru crudini-0.7/tox.ini crudini-0.9.3/tox.ini --- crudini-0.7/tox.ini 2014-11-28 16:14:35.000000000 +0200 +++ crudini-0.9.3/tox.ini 2016-11-26 13:16:39.000000000 +0200 @@ -2,8 +2,8 @@ envlist = py26,py27,pep8 [testenv] -deps=iniparse -commands=/bin/bash -c 'cd tests && ./test.sh' +deps = iniparse>=0.3.2 +commands = /bin/bash -c 'cd tests && ./test.sh' [testenv:pep8] deps = flake8