class StreamOpener(object): """Provides a transport vehicle to open the formatter output stream when the formatter needs it. In addition, it provides the formatter with more control: * when a stream is opened * if a stream is opened at all * the name (filename/dirname) of the output stream * let it decide if directory mode is used instead of file mode """ # FORMER: default_encoding = "UTF-8" default_encoding = select_best_encoding() def __init__(self, filename=None, stream=None, encoding=None): if not encoding: encoding = self.default_encoding if stream: stream = self.ensure_stream_with_encoder(stream, encoding) self.name = filename self.stream = stream self.encoding = encoding self.should_close_stream = not stream # Only for not pre-opened ones. @staticmethod def ensure_dir_exists(directory): if directory and not os.path.isdir(directory): os.makedirs(directory) @classmethod def ensure_stream_with_encoder(cls, stream, encoding=None): return _ensure_stream_with_encoder(stream, encoding) def open(self): if not self.stream or self.stream.closed: self.ensure_dir_exists(os.path.dirname(self.name)) stream = open(self.name, "w") # stream = codecs.open(self.name, "w", encoding=self.encoding) stream = self.ensure_stream_with_encoder(stream, self.encoding) self.stream = stream # -- Keep stream for house-keeping. self.should_close_stream = True assert self.should_close_stream return self.stream def close(self): """ Close the stream, if it was opened by this stream_opener. Skip closing for sys.stdout and pre-opened streams. :return: True, if stream was closed. """ closed = False if self.stream and self.should_close_stream: closed = getattr(self.stream, "closed", False) if not closed: self.stream.close() closed = True self.stream = None return closed
def explore_platform_encoding(): python_version = platform.python_version() print("python %s (platform: %s, %s, %s)" % (python_version, sys.platform, platform.python_implementation(), platform.platform())) print("sys.getfilesystemencoding(): %s" % sys.getfilesystemencoding()) print("locale.getpreferredencoding(): %s" % locale.getpreferredencoding()) print("behave.textutil.select_best_encoding(): %s" % select_best_encoding()) return 0
def __init__(self, command_args=None, load_config=True, verbose=None, **kwargs): """ Constructs a behave configuration object. * loads the configuration defaults (if needed). * process the command-line args * store the configuration results :param command_args: Provide command args (as sys.argv). If command_args is None, sys.argv[1:] is used. :type command_args: list<str>, str :param load_config: Indicate if configfile should be loaded (=true) :param verbose: Indicate if diagnostic output is enabled :param kwargs: Used to hand-over/overwrite default values. """ # pylint: disable=too-many-branches, too-many-statements if command_args is None: command_args = sys.argv[1:] elif isinstance(command_args, six.string_types): encoding = select_best_encoding() or "utf-8" if six.PY2 and isinstance(command_args, six.text_type): command_args = command_args.encode(encoding) elif six.PY3 and isinstance(command_args, six.binary_type): command_args = command_args.decode(encoding) command_args = shlex.split(command_args) elif isinstance(command_args, (list, tuple)): command_args = to_texts(command_args) if verbose is None: # -- AUTO-DISCOVER: Verbose mode from command-line args. verbose = ("-v" in command_args) or ("--verbose" in command_args) self.version = None self.tags_help = None self.lang_list = None self.lang_help = None self.default_tags = None self.junit = None self.logging_format = None self.logging_datefmt = None self.name = None self.scope = None self.steps_catalog = None self.userdata = None self.wip = None defaults = self.defaults.copy() for name, value in six.iteritems(kwargs): defaults[name] = value self.defaults = defaults self.formatters = [] self.reporters = [] self.name_re = None self.outputs = [] self.include_re = None self.exclude_re = None self.scenario_outline_annotation_schema = None # pylint: disable=invalid-name self.steps_dir = "steps" self.environment_file = "environment.py" self.userdata_defines = None self.more_formatters = None if load_config: load_configuration(self.defaults, verbose=verbose) parser = setup_parser() parser.set_defaults(**self.defaults) args = parser.parse_args(command_args) for key, value in six.iteritems(args.__dict__): if key.startswith("_") and key not in self.cmdline_only_options: continue setattr(self, key, value) self.paths = [os.path.normpath(path) for path in self.paths] self.setup_outputs(args.outfiles) if self.steps_catalog: # -- SHOW STEP-CATALOG: As step summary. self.default_format = "steps.catalog" self.format = ["steps.catalog"] self.dry_run = True self.summary = False self.show_skipped = False self.quiet = True if self.wip: # Only run scenarios tagged with "wip". # Additionally: # * use the "plain" formatter (per default) # * do not capture stdout or logging output and # * stop at the first failure. self.default_format = "plain" self.tags = ["wip"] + self.default_tags.split() self.color = False self.stop = True self.log_capture = False self.stdout_capture = False self.tags = TagExpression(self.tags or self.default_tags.split()) if self.quiet: self.show_source = False self.show_snippets = False if self.exclude_re: self.exclude_re = re.compile(self.exclude_re) if self.include_re: self.include_re = re.compile(self.include_re) if self.name: # -- SELECT: Scenario-by-name, build regular expression. self.name_re = self.build_name_re(self.name) if self.stage is None: # pylint: disable=access-member-before-definition # -- USE ENVIRONMENT-VARIABLE, if stage is undefined. self.stage = os.environ.get("BEHAVE_STAGE", None) self.setup_stage(self.stage) self.setup_model() self.setup_userdata() # -- FINALLY: Setup Reporters and Formatters # NOTE: Reporters and Formatters can now use userdata information. if self.junit: # Buffer the output (it will be put into Junit report) self.stdout_capture = True self.stderr_capture = True self.log_capture = True self.reporters.append(JUnitReporter(self)) if self.summary: self.reporters.append(SummaryReporter(self)) self.setup_formats() unknown_formats = self.collect_unknown_formats() if unknown_formats: parser.error("format=%s is unknown" % ", ".join(unknown_formats))
def __init__(self, command_args=None, load_config=True, verbose=None, **kwargs): """ Constructs a behave configuration object. * loads the configuration defaults (if needed). * process the command-line args * store the configuration results :param command_args: Provide command args (as sys.argv). If command_args is None, sys.argv[1:] is used. :type command_args: list<str>, str :param load_config: Indicate if configfile should be loaded (=true) :param verbose: Indicate if diagnostic output is enabled :param kwargs: Used to hand-over/overwrite default values. """ # pylint: disable=too-many-branches, too-many-statements if command_args is None: command_args = sys.argv[1:] elif isinstance(command_args, six.string_types): encoding = select_best_encoding() or "utf-8" if six.PY2 and isinstance(command_args, six.text_type): command_args = command_args.encode(encoding) elif six.PY3 and isinstance(command_args, six.binary_type): command_args = command_args.decode(encoding) command_args = shlex.split(command_args) elif isinstance(command_args, (list, tuple)): command_args = to_texts(command_args) if verbose is None: # -- AUTO-DISCOVER: Verbose mode from command-line args. verbose = ("-v" in command_args) or ("--verbose" in command_args) self.version = None self.tags_help = None self.lang_list = None self.lang_help = None self.default_tags = None self.junit = None self.logging_format = None self.logging_datefmt = None self.name = None self.scope = None self.steps_catalog = None self.userdata = None self.wip = None defaults = self.defaults.copy() for name, value in six.iteritems(kwargs): defaults[name] = value self.defaults = defaults self.formatters = [] self.reporters = [] self.name_re = None self.outputs = [] self.include_re = None self.exclude_re = None self.scenario_outline_annotation_schema = None # pylint: disable=invalid-name self.steps_dir = "steps" self.environment_file = "environment.py" self.userdata_defines = None self.more_formatters = None if load_config: load_configuration(self.defaults, verbose=verbose) parser = setup_parser() parser.set_defaults(**self.defaults) args = parser.parse_args(command_args) for key, value in six.iteritems(args.__dict__): if key.startswith("_") and key not in self.cmdline_only_options: continue setattr(self, key, value) # -- ATTRIBUTE-NAME-CLEANUP: self.tag_expression = None self._tags = self.tags self.tags = None if isinstance(self.default_tags, six.string_types): self.default_tags = self.default_tags.split() self.paths = [os.path.normpath(path) for path in self.paths] self.setup_outputs(args.outfiles) if self.steps_catalog: # -- SHOW STEP-CATALOG: As step summary. self.default_format = "steps.catalog" self.format = ["steps.catalog"] self.dry_run = True self.summary = False self.show_skipped = False self.quiet = True if self.wip: # Only run scenarios tagged with "wip". # Additionally: # * use the "plain" formatter (per default) # * do not capture stdout or logging output and # * stop at the first failure. self.default_format = "plain" self._tags = ["wip"] + self.default_tags self.color = False self.stop = True self.log_capture = False self.stdout_capture = False self.tag_expression = make_tag_expression(self._tags or self.default_tags) # -- BACKWARD-COMPATIBLE (BAD-NAMING STYLE; deprecating): self.tags = self.tag_expression if self.quiet: self.show_source = False self.show_snippets = False if self.exclude_re: self.exclude_re = re.compile(self.exclude_re) if self.include_re: self.include_re = re.compile(self.include_re) if self.name: # -- SELECT: Scenario-by-name, build regular expression. self.name_re = self.build_name_re(self.name) if self.stage is None: # pylint: disable=access-member-before-definition # -- USE ENVIRONMENT-VARIABLE, if stage is undefined. self.stage = os.environ.get("BEHAVE_STAGE", None) self.setup_stage(self.stage) self.setup_model() self.setup_userdata() # -- FINALLY: Setup Reporters and Formatters # NOTE: Reporters and Formatters can now use userdata information. if self.junit: # Buffer the output (it will be put into Junit report) self.stdout_capture = True self.stderr_capture = True self.log_capture = True self.reporters.append(JUnitReporter(self)) if self.summary: self.reporters.append(SummaryReporter(self)) self.setup_formats() unknown_formats = self.collect_unknown_formats() if unknown_formats: parser.error("format=%s is unknown" % ", ".join(unknown_formats))