diff options
author | pravindalve | 2020-02-14 13:04:30 +0530 |
---|---|---|
committer | GitHub | 2020-02-14 13:04:30 +0530 |
commit | a80b6726f5f70d9a2ec1cbf361e7f607849343bf (patch) | |
tree | 333d34f58255003939e70b800d2cd57e40253b6b /venv/Lib/site-packages/pylint/utils | |
parent | 8189de7d424964aac11b81c8297b7af7fcedd2b8 (diff) | |
parent | df141f35dccc6b21fcfa575707c6435a39d0002f (diff) | |
download | Chemical-Simulator-GUI-a80b6726f5f70d9a2ec1cbf361e7f607849343bf.tar.gz Chemical-Simulator-GUI-a80b6726f5f70d9a2ec1cbf361e7f607849343bf.tar.bz2 Chemical-Simulator-GUI-a80b6726f5f70d9a2ec1cbf361e7f607849343bf.zip |
Merge pull request #2 from pravindalve/master
Code restructured, some ui improvizations, undo redo implementation and Binary envelops utility
Diffstat (limited to 'venv/Lib/site-packages/pylint/utils')
-rw-r--r-- | venv/Lib/site-packages/pylint/utils/__init__.py | 64 | ||||
-rw-r--r-- | venv/Lib/site-packages/pylint/utils/__pycache__/__init__.cpython-37.pyc | bin | 0 -> 869 bytes | |||
-rw-r--r-- | venv/Lib/site-packages/pylint/utils/__pycache__/ast_walker.cpython-37.pyc | bin | 0 -> 2078 bytes | |||
-rw-r--r-- | venv/Lib/site-packages/pylint/utils/__pycache__/file_state.cpython-37.pyc | bin | 0 -> 3852 bytes | |||
-rw-r--r-- | venv/Lib/site-packages/pylint/utils/__pycache__/utils.cpython-37.pyc | bin | 0 -> 10339 bytes | |||
-rw-r--r-- | venv/Lib/site-packages/pylint/utils/ast_walker.py | 79 | ||||
-rw-r--r-- | venv/Lib/site-packages/pylint/utils/file_state.py | 138 | ||||
-rw-r--r-- | venv/Lib/site-packages/pylint/utils/utils.py | 371 |
8 files changed, 652 insertions, 0 deletions
diff --git a/venv/Lib/site-packages/pylint/utils/__init__.py b/venv/Lib/site-packages/pylint/utils/__init__.py new file mode 100644 index 0000000..8ee9e07 --- /dev/null +++ b/venv/Lib/site-packages/pylint/utils/__init__.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr> +# Copyright (c) 2009 Vincent +# Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com> +# Copyright (c) 2012-2014 Google, Inc. +# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com> +# Copyright (c) 2014-2015 Michal Nowikowski <godfryd@gmail.com> +# Copyright (c) 2014 LCD 47 <lcd047@gmail.com> +# Copyright (c) 2014 Brett Cannon <brett@python.org> +# Copyright (c) 2014 Arun Persaud <arun@nubati.net> +# Copyright (c) 2014 Damien Nozay <damien.nozay@gmail.com> +# Copyright (c) 2015 Aru Sahni <arusahni@gmail.com> +# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org> +# Copyright (c) 2015 Simu Toni <simutoni@gmail.com> +# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro> +# Copyright (c) 2016 Łukasz Rogalski <rogalski.91@gmail.com> +# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com> +# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net> +# Copyright (c) 2016 Glenn Matthews <glmatthe@cisco.com> +# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk> +# Copyright (c) 2016 xmo-odoo <xmo-odoo@users.noreply.github.com> +# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com> +# Copyright (c) 2017 Pierre Sassoulas <pierre.sassoulas@cea.fr> +# Copyright (c) 2017 Bryce Guinta <bryce.paul.guinta@gmail.com> +# Copyright (c) 2017 Chris Lamb <chris@chris-lamb.co.uk> +# Copyright (c) 2017 Anthony Sottile <asottile@umich.edu> +# Copyright (c) 2017 Thomas Hisch <t.hisch@gmail.com> +# Copyright (c) 2017 Mikhail Fesenko <proggga@gmail.com> +# Copyright (c) 2017 Craig Citro <craigcitro@gmail.com> +# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi> +# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com> +# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com> +# Copyright (c) 2018 Pierre Sassoulas <pierre.sassoulas@wisebim.fr> +# Copyright (c) 2018 Reverb C <reverbc@users.noreply.github.com> +# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com> + +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/master/COPYING + +"""some various utilities and helper classes, most of them used in the +main pylint class +""" + +from pylint.utils.ast_walker import ASTWalker +from pylint.utils.file_state import FileState +from pylint.utils.utils import ( + _basename_in_blacklist_re, + _check_csv, + _format_option_value, + _splitstrip, + _unquote, + decoding_stream, + deprecated_option, + expand_modules, + format_section, + get_global_option, + get_module_and_frameid, + get_rst_section, + get_rst_title, + normalize_text, + register_plugins, + safe_decode, + tokenize_module, +) diff --git a/venv/Lib/site-packages/pylint/utils/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pylint/utils/__pycache__/__init__.cpython-37.pyc Binary files differnew file mode 100644 index 0000000..6f3569d --- /dev/null +++ b/venv/Lib/site-packages/pylint/utils/__pycache__/__init__.cpython-37.pyc diff --git a/venv/Lib/site-packages/pylint/utils/__pycache__/ast_walker.cpython-37.pyc b/venv/Lib/site-packages/pylint/utils/__pycache__/ast_walker.cpython-37.pyc Binary files differnew file mode 100644 index 0000000..af27609 --- /dev/null +++ b/venv/Lib/site-packages/pylint/utils/__pycache__/ast_walker.cpython-37.pyc diff --git a/venv/Lib/site-packages/pylint/utils/__pycache__/file_state.cpython-37.pyc b/venv/Lib/site-packages/pylint/utils/__pycache__/file_state.cpython-37.pyc Binary files differnew file mode 100644 index 0000000..4a43508 --- /dev/null +++ b/venv/Lib/site-packages/pylint/utils/__pycache__/file_state.cpython-37.pyc diff --git a/venv/Lib/site-packages/pylint/utils/__pycache__/utils.cpython-37.pyc b/venv/Lib/site-packages/pylint/utils/__pycache__/utils.cpython-37.pyc Binary files differnew file mode 100644 index 0000000..9049995 --- /dev/null +++ b/venv/Lib/site-packages/pylint/utils/__pycache__/utils.cpython-37.pyc diff --git a/venv/Lib/site-packages/pylint/utils/ast_walker.py b/venv/Lib/site-packages/pylint/utils/ast_walker.py new file mode 100644 index 0000000..2e7a6da --- /dev/null +++ b/venv/Lib/site-packages/pylint/utils/ast_walker.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- + +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/master/COPYING + +import collections + +from astroid import nodes + + +class ASTWalker: + def __init__(self, linter): + # callbacks per node types + self.nbstatements = 0 + self.visit_events = collections.defaultdict(list) + self.leave_events = collections.defaultdict(list) + self.linter = linter + + def _is_method_enabled(self, method): + if not hasattr(method, "checks_msgs"): + return True + for msg_desc in method.checks_msgs: + if self.linter.is_message_enabled(msg_desc): + return True + return False + + def add_checker(self, checker): + """walk to the checker's dir and collect visit and leave methods""" + vcids = set() + lcids = set() + visits = self.visit_events + leaves = self.leave_events + for member in dir(checker): + cid = member[6:] + if cid == "default": + continue + if member.startswith("visit_"): + v_meth = getattr(checker, member) + # don't use visit_methods with no activated message: + if self._is_method_enabled(v_meth): + visits[cid].append(v_meth) + vcids.add(cid) + elif member.startswith("leave_"): + l_meth = getattr(checker, member) + # don't use leave_methods with no activated message: + if self._is_method_enabled(l_meth): + leaves[cid].append(l_meth) + lcids.add(cid) + visit_default = getattr(checker, "visit_default", None) + if visit_default: + for cls in nodes.ALL_NODE_CLASSES: + cid = cls.__name__.lower() + if cid not in vcids: + visits[cid].append(visit_default) + # for now we have no "leave_default" method in Pylint + + def walk(self, astroid): + """call visit events of astroid checkers for the given node, recurse on + its children, then leave events. + """ + cid = astroid.__class__.__name__.lower() + + # Detect if the node is a new name for a deprecated alias. + # In this case, favour the methods for the deprecated + # alias if any, in order to maintain backwards + # compatibility. + visit_events = self.visit_events.get(cid, ()) + leave_events = self.leave_events.get(cid, ()) + + if astroid.is_statement: + self.nbstatements += 1 + # generate events for this node on each checker + for callback in visit_events or (): + callback(astroid) + # recurse on children + for child in astroid.get_children(): + self.walk(child) + for callback in leave_events or (): + callback(astroid) diff --git a/venv/Lib/site-packages/pylint/utils/file_state.py b/venv/Lib/site-packages/pylint/utils/file_state.py new file mode 100644 index 0000000..1a8dd4d --- /dev/null +++ b/venv/Lib/site-packages/pylint/utils/file_state.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- + +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/master/COPYING + +import collections + +from astroid import nodes + +from pylint.constants import MSG_STATE_SCOPE_MODULE, WarningScope + + +class FileState: + """Hold internal state specific to the currently analyzed file""" + + def __init__(self, modname=None): + self.base_name = modname + self._module_msgs_state = {} + self._raw_module_msgs_state = {} + self._ignored_msgs = collections.defaultdict(set) + self._suppression_mapping = {} + self._effective_max_line_number = None + + def collect_block_lines(self, msgs_store, module_node): + """Walk the AST to collect block level options line numbers.""" + for msg, lines in self._module_msgs_state.items(): + self._raw_module_msgs_state[msg] = lines.copy() + orig_state = self._module_msgs_state.copy() + self._module_msgs_state = {} + self._suppression_mapping = {} + self._effective_max_line_number = module_node.tolineno + self._collect_block_lines(msgs_store, module_node, orig_state) + + def _collect_block_lines(self, msgs_store, node, msg_state): + """Recursively walk (depth first) AST to collect block level options + line numbers. + """ + for child in node.get_children(): + self._collect_block_lines(msgs_store, child, msg_state) + first = node.fromlineno + last = node.tolineno + # first child line number used to distinguish between disable + # which are the first child of scoped node with those defined later. + # For instance in the code below: + # + # 1. def meth8(self): + # 2. """test late disabling""" + # 3. pylint: disable=not-callable + # 4. print(self.blip) + # 5. pylint: disable=no-member + # 6. print(self.bla) + # + # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6 + # + # this is necessary to disable locally messages applying to class / + # function using their fromlineno + if ( + isinstance(node, (nodes.Module, nodes.ClassDef, nodes.FunctionDef)) + and node.body + ): + firstchildlineno = node.body[0].fromlineno + else: + firstchildlineno = last + for msgid, lines in msg_state.items(): + for lineno, state in list(lines.items()): + original_lineno = lineno + if first > lineno or last < lineno: + continue + # Set state for all lines for this block, if the + # warning is applied to nodes. + message_definitions = msgs_store.get_message_definitions(msgid) + for message_definition in message_definitions: + if message_definition.scope == WarningScope.NODE: + if lineno > firstchildlineno: + state = True + first_, last_ = node.block_range(lineno) + else: + first_ = lineno + last_ = last + for line in range(first_, last_ + 1): + # do not override existing entries + if line in self._module_msgs_state.get(msgid, ()): + continue + if line in lines: # state change in the same block + state = lines[line] + original_lineno = line + if not state: + self._suppression_mapping[(msgid, line)] = original_lineno + try: + self._module_msgs_state[msgid][line] = state + except KeyError: + self._module_msgs_state[msgid] = {line: state} + del lines[lineno] + + def set_msg_status(self, msg, line, status): + """Set status (enabled/disable) for a given message at a given line""" + assert line > 0 + try: + self._module_msgs_state[msg.msgid][line] = status + except KeyError: + self._module_msgs_state[msg.msgid] = {line: status} + + def handle_ignored_message( + self, state_scope, msgid, line, node, args, confidence + ): # pylint: disable=unused-argument + """Report an ignored message. + + state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG, + depending on whether the message was disabled locally in the module, + or globally. The other arguments are the same as for add_message. + """ + if state_scope == MSG_STATE_SCOPE_MODULE: + try: + orig_line = self._suppression_mapping[(msgid, line)] + self._ignored_msgs[(msgid, orig_line)].add(line) + except KeyError: + pass + + def iter_spurious_suppression_messages(self, msgs_store): + for warning, lines in self._raw_module_msgs_state.items(): + for line, enable in lines.items(): + if not enable and (warning, line) not in self._ignored_msgs: + # ignore cyclic-import check which can show false positives + # here due to incomplete context + if warning != "R0401": + yield "useless-suppression", line, ( + msgs_store.get_msg_display_string(warning), + ) + # don't use iteritems here, _ignored_msgs may be modified by add_message + for (warning, from_), lines in list(self._ignored_msgs.items()): + for line in lines: + yield "suppressed-message", line, ( + msgs_store.get_msg_display_string(warning), + from_, + ) + + def get_effective_max_line_number(self): + return self._effective_max_line_number diff --git a/venv/Lib/site-packages/pylint/utils/utils.py b/venv/Lib/site-packages/pylint/utils/utils.py new file mode 100644 index 0000000..5605ecd --- /dev/null +++ b/venv/Lib/site-packages/pylint/utils/utils.py @@ -0,0 +1,371 @@ +# -*- coding: utf-8 -*- + +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/master/COPYING + +import codecs +import re +import sys +import textwrap +import tokenize +from os import linesep, listdir +from os.path import basename, dirname, exists, isdir, join, normpath, splitext + +from astroid import Module, modutils + +from pylint.constants import PY_EXTS + + +def normalize_text(text, line_len=80, indent=""): + """Wrap the text on the given line length.""" + return "\n".join( + textwrap.wrap( + text, width=line_len, initial_indent=indent, subsequent_indent=indent + ) + ) + + +def get_module_and_frameid(node): + """return the module name and the frame id in the module""" + frame = node.frame() + module, obj = "", [] + while frame: + if isinstance(frame, Module): + module = frame.name + else: + obj.append(getattr(frame, "name", "<lambda>")) + try: + frame = frame.parent.frame() + except AttributeError: + frame = None + obj.reverse() + return module, ".".join(obj) + + +def get_rst_title(title, character): + """Permit to get a title formatted as ReStructuredText test (underlined with a chosen character).""" + return "%s\n%s\n" % (title, character * len(title)) + + +def get_rst_section(section, options, doc=None): + """format an options section using as a ReStructuredText formatted output""" + result = "" + if section: + result += get_rst_title(section, "'") + if doc: + formatted_doc = normalize_text(doc, line_len=79, indent="") + result += "%s\n\n" % formatted_doc + for optname, optdict, value in options: + help_opt = optdict.get("help") + result += ":%s:\n" % optname + if help_opt: + formatted_help = normalize_text(help_opt, line_len=79, indent=" ") + result += "%s\n" % formatted_help + if value: + value = str(_format_option_value(optdict, value)) + result += "\n Default: ``%s``\n" % value.replace("`` ", "```` ``") + return result + + +def safe_decode(line, encoding, *args, **kwargs): + """return decoded line from encoding or decode with default encoding""" + try: + return line.decode(encoding or sys.getdefaultencoding(), *args, **kwargs) + except LookupError: + return line.decode(sys.getdefaultencoding(), *args, **kwargs) + + +def decoding_stream(stream, encoding, errors="strict"): + try: + reader_cls = codecs.getreader(encoding or sys.getdefaultencoding()) + except LookupError: + reader_cls = codecs.getreader(sys.getdefaultencoding()) + return reader_cls(stream, errors) + + +def tokenize_module(module): + with module.stream() as stream: + readline = stream.readline + return list(tokenize.tokenize(readline)) + + +def _basename_in_blacklist_re(base_name, black_list_re): + """Determines if the basename is matched in a regex blacklist + + :param str base_name: The basename of the file + :param list black_list_re: A collection of regex patterns to match against. + Successful matches are blacklisted. + + :returns: `True` if the basename is blacklisted, `False` otherwise. + :rtype: bool + """ + for file_pattern in black_list_re: + if file_pattern.match(base_name): + return True + return False + + +def _modpath_from_file(filename, is_namespace): + def _is_package_cb(path, parts): + return modutils.check_modpath_has_init(path, parts) or is_namespace + + return modutils.modpath_from_file_with_callback( + filename, is_package_cb=_is_package_cb + ) + + +def expand_modules(files_or_modules, black_list, black_list_re): + """take a list of files/modules/packages and return the list of tuple + (file, module name) which have to be actually checked + """ + result = [] + errors = [] + for something in files_or_modules: + if basename(something) in black_list: + continue + if _basename_in_blacklist_re(basename(something), black_list_re): + continue + if exists(something): + # this is a file or a directory + try: + modname = ".".join(modutils.modpath_from_file(something)) + except ImportError: + modname = splitext(basename(something))[0] + if isdir(something): + filepath = join(something, "__init__.py") + else: + filepath = something + else: + # suppose it's a module or package + modname = something + try: + filepath = modutils.file_from_modpath(modname.split(".")) + if filepath is None: + continue + except (ImportError, SyntaxError) as ex: + # The SyntaxError is a Python bug and should be + # removed once we move away from imp.find_module: http://bugs.python.org/issue10588 + errors.append({"key": "fatal", "mod": modname, "ex": ex}) + continue + + filepath = normpath(filepath) + modparts = (modname or something).split(".") + + try: + spec = modutils.file_info_from_modpath(modparts, path=sys.path) + except ImportError: + # Might not be acceptable, don't crash. + is_namespace = False + is_directory = isdir(something) + else: + is_namespace = modutils.is_namespace(spec) + is_directory = modutils.is_directory(spec) + + if not is_namespace: + result.append( + { + "path": filepath, + "name": modname, + "isarg": True, + "basepath": filepath, + "basename": modname, + } + ) + + has_init = ( + not (modname.endswith(".__init__") or modname == "__init__") + and basename(filepath) == "__init__.py" + ) + + if has_init or is_namespace or is_directory: + for subfilepath in modutils.get_module_files( + dirname(filepath), black_list, list_all=is_namespace + ): + if filepath == subfilepath: + continue + if _basename_in_blacklist_re(basename(subfilepath), black_list_re): + continue + + modpath = _modpath_from_file(subfilepath, is_namespace) + submodname = ".".join(modpath) + result.append( + { + "path": subfilepath, + "name": submodname, + "isarg": False, + "basepath": filepath, + "basename": modname, + } + ) + return result, errors + + +def register_plugins(linter, directory): + """load all module and package in the given directory, looking for a + 'register' function in each one, used to register pylint checkers + """ + imported = {} + for filename in listdir(directory): + base, extension = splitext(filename) + if base in imported or base == "__pycache__": + continue + if ( + extension in PY_EXTS + and base != "__init__" + or (not extension and isdir(join(directory, base))) + ): + try: + module = modutils.load_module_from_file(join(directory, filename)) + except ValueError: + # empty module name (usually emacs auto-save files) + continue + except ImportError as exc: + print( + "Problem importing module %s: %s" % (filename, exc), file=sys.stderr + ) + else: + if hasattr(module, "register"): + module.register(linter) + imported[base] = 1 + + +def get_global_option(checker, option, default=None): + """ Retrieve an option defined by the given *checker* or + by all known option providers. + + It will look in the list of all options providers + until the given *option* will be found. + If the option wasn't found, the *default* value will be returned. + """ + # First, try in the given checker's config. + # After that, look in the options providers. + + try: + return getattr(checker.config, option.replace("-", "_")) + except AttributeError: + pass + for provider in checker.linter.options_providers: + for options in provider.options: + if options[0] == option: + return getattr(provider.config, option.replace("-", "_")) + return default + + +def deprecated_option( + shortname=None, opt_type=None, help_msg=None, deprecation_msg=None +): + def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument + if deprecation_msg: + sys.stderr.write(deprecation_msg % (optname,)) + + option = { + "help": help_msg, + "hide": True, + "type": opt_type, + "action": "callback", + "callback": _warn_deprecated, + "deprecated": True, + } + if shortname: + option["shortname"] = shortname + return option + + +def _splitstrip(string, sep=","): + """return a list of stripped string by splitting the string given as + argument on `sep` (',' by default). Empty string are discarded. + + >>> _splitstrip('a, b, c , 4,,') + ['a', 'b', 'c', '4'] + >>> _splitstrip('a') + ['a'] + >>> _splitstrip('a,\nb,\nc,') + ['a', 'b', 'c'] + + :type string: str or unicode + :param string: a csv line + + :type sep: str or unicode + :param sep: field separator, default to the comma (',') + + :rtype: str or unicode + :return: the unquoted string (or the input string if it wasn't quoted) + """ + return [word.strip() for word in string.split(sep) if word.strip()] + + +def _unquote(string): + """remove optional quotes (simple or double) from the string + + :type string: str or unicode + :param string: an optionally quoted string + + :rtype: str or unicode + :return: the unquoted string (or the input string if it wasn't quoted) + """ + if not string: + return string + if string[0] in "\"'": + string = string[1:] + if string[-1] in "\"'": + string = string[:-1] + return string + + +def _check_csv(value): + if isinstance(value, (list, tuple)): + return value + return _splitstrip(value) + + +def _comment(string): + """return string as a comment""" + lines = [line.strip() for line in string.splitlines()] + return "# " + ("%s# " % linesep).join(lines) + + +def _format_option_value(optdict, value): + """return the user input's value from a 'compiled' value""" + if isinstance(value, (list, tuple)): + value = ",".join(_format_option_value(optdict, item) for item in value) + elif isinstance(value, dict): + value = ",".join("%s:%s" % (k, v) for k, v in value.items()) + elif hasattr(value, "match"): # optdict.get('type') == 'regexp' + # compiled regexp + value = value.pattern + elif optdict.get("type") == "yn": + value = "yes" if value else "no" + elif isinstance(value, str) and value.isspace(): + value = "'%s'" % value + return value + + +def format_section(stream, section, options, doc=None): + """format an options section using the INI format""" + if doc: + print(_comment(doc), file=stream) + print("[%s]" % section, file=stream) + _ini_format(stream, options) + + +def _ini_format(stream, options): + """format options using the INI format""" + for optname, optdict, value in options: + value = _format_option_value(optdict, value) + help_opt = optdict.get("help") + if help_opt: + help_opt = normalize_text(help_opt, line_len=79, indent="# ") + print(file=stream) + print(help_opt, file=stream) + else: + print(file=stream) + if value is None: + print("#%s=" % optname, file=stream) + else: + value = str(value).strip() + if re.match(r"^([\w-]+,)+[\w-]+$", str(value)): + separator = "\n " + " " * len(optname) + value = separator.join(x + "," for x in str(value).split(",")) + # remove trailing ',' from last element of the list + value = value[:-1] + print("%s=%s" % (optname, value), file=stream) |