#1695 cli: fix output format, new option --output-format
Merged 2 years ago by praiskup. Opened 2 years ago by schlupov.
copr/ schlupov/copr fix_cli_list_builds  into  main

file modified
+150 -7
@@ -74,6 +74,13 @@ 

  

  """

  

+ output_format_help = """

+ Set the formatting style. We recommend using json, which prints the required data

+ in json format. The text format prints the required data in a column, one piece of

+ information per line. The text-row format prints all information separated

+ by a space on a single line.

+ """

+ 

  try:

      input = raw_input

  except NameError:
@@ -125,6 +132,108 @@ 

      return buildopts

  

  

+ class AbstractPrinter(object):

+     """Abstract class defining mandatory methods of printer classes"""

+ 

+     def __init__(self, fields):

+         """Represents the data we want to print.

+         Supports callable lambda function which takes exactly one argument"""

+         self.fields = fields

+ 

+     def add_data(self, data):

+         """Initialize the data to be printed"""

+         raise NotImplementedError

+ 

+     def finish(self):

+         """Print the data according to the set format"""

+         raise NotImplementedError

+ 

+ 

+ class RowTextPrinter(AbstractPrinter):

+     """The class takes care of printing the data in row text format"""

+ 

+     def finish(self):

+         pass

+ 

+     def add_data(self, data):

+         row_data = []

+         for field in self.fields:

+             if callable(field):

+                 row_data.append(str(field(data)))

+             else:

+                 row_data.append(str(data[field]))

+         print("\t".join(row_data))

+ 

+ 

+ class ColumnTextPrinter(AbstractPrinter):

+     """The class takes care of printing the data in column text format"""

+ 

+     first_line = True

+ 

+     def finish(self):

+         pass

+ 

+     def add_data(self, data):

+         if not self.first_line:

+             print()

+         self.first_line = False

+         for field in self.fields:

+             if callable(field):

+                 print("{0}: {1}".format(field.__code__.co_varnames[0], str(field(data))))

+             else:

+                 print("{0}: {1}".format(field, str(data[field])))

+ 

+ 

+ class JsonPrinter(AbstractPrinter):

+     """The class takes care of printing the data in json format"""

+ 

+     def __init__(self, fields):

+         super(JsonPrinter, self).__init__(fields)

+         self.output_info = []

+         self.json_values_list = []

+ 

+     def __process_fields(self, data):

+         desired_info = []

+         for field in self.fields:

+             if callable(field):

+                 desired_info.append(field(data))

+             else:

+                 desired_info.append(data[field])

+         self.output_info.append(desired_info)

+ 

+     def add_data(self, data):

+         json_values = {}

+         self.__process_fields(data)

+         for item in self.output_info:

+             for field, info in zip(self.fields, item):

+                 if callable(field):

+                     json_values[field.__code__.co_varnames[0]] = info

+                 else:

+                     json_values[field] = info

+         self.json_values_list.append(json_values)

+ 

+     def finish(self):

+         print(json_dumps(self.json_values_list[0]))

+ 

+ 

+ class JsonPrinterListCommand(JsonPrinter):

+     """The class takes care of printing the data in list in json format"""

+ 

+     def finish(self):

+         print(json_dumps(self.json_values_list))

+ 

+ 

+ def get_printer(output_format, fields, list_command=False):

+     """According to output_format decide which object of printer to return"""

+     if output_format == "json":

+         if list_command:

+             return JsonPrinterListCommand(fields)

+         return JsonPrinter(fields)

+     if output_format == "text":

+         return ColumnTextPrinter(fields)

+     return RowTextPrinter(fields)

+ 

+ 

  class Commands(object):

      def __init__(self, config_path):

          self.config_path = config_path or '~/.config/copr'
@@ -521,10 +630,17 @@ 

  

          builds_list = self.client.build_proxy.get_list(ownername, projectname,

                                                         pagination=pagination)

+         if not args.output_format:

+             args.output_format = "text-row"

+             sys.stderr.write(

+                 "The default setting will be changed from text-row to json in the following releases\n")

+ 

          while builds_list:

-             for build in builds_list:

-                 print("{0}\t{1}\t{2}".format(build["id"], build["source_package"]["name"],

-                                              build["state"]))

+             fields = ["id", lambda name: name["source_package"]["name"], "state"]

+             printer = get_printer(args.output_format, fields, True)

+             for data in builds_list:

+                 printer.add_data(data)

+             printer.finish()

              builds_list = next_page(builds_list)

  

      def action_mock_config(self, args):
@@ -643,7 +759,12 @@ 

          project_chroot = self.client.project_chroot_proxy.get(

              ownername=owner, projectname=copr, chrootname=chroot

          )

-         print(json_dumps(project_chroot))

+         fields = ["additional_packages", "additional_repos", "comps_name", "delete_after_days",

+                   "isolation", "mock_chroot", "ownername", "projectname", "with_opts",

+                   "without_opts"]

+         printer = get_printer(args.output_format, fields)

+         printer.add_data(project_chroot)

+         printer.finish()

  

      def action_list_chroots(self, args):

          """List all currently available chroots.
@@ -759,7 +880,14 @@ 

                                                        with_latest_build=args.with_latest_build,

                                                        with_latest_succeeded_build=args.with_latest_succeeded_build)

          packages_with_builds = [self._package_with_builds(p, args) for p in packages]

-         print(json_dumps(packages_with_builds))

+         fields = ["id", "auto_rebuild", "name", "ownername", "projectname", "source_dict",

+                   "source_type", "latest_succeeded_build", "latest_build"]

+         if args.with_all_builds:

+             fields.insert(1, "builds")

+         printer = get_printer(args.output_format, fields, True)

+         for data in packages_with_builds:

+             printer.add_data(data)

+         printer.finish()

  

      def action_list_package_names(self, args):

          ownername, projectname = self.parse_name(args.copr)
@@ -777,7 +905,13 @@ 

              with_latest_succeeded_build=args.with_latest_succeeded_build,

          )

          package = self._package_with_builds(package, args)

-         print(json_dumps(package))

+         fields = ["auto_rebuild", "id", "latest_build", "latest_succeeded_build", "name", "ownername",

+                   "projectname", "source_dict", "source_type"]

+         if args.with_all_builds:

+             fields.insert(1, "builds")

+         printer = get_printer(args.output_format, fields)

+         printer.add_data(package)

+         printer.finish()

  

      def _package_with_builds(self, package, args):

          ownername, projectname = self.parse_name(args.copr)
@@ -1076,6 +1210,8 @@ 

      parser_builds.add_argument("project", help="Which project's builds should be listed.\

                                 Can be just a name of the project or even in format\

                                 username/project or @groupname/project.")

+     parser_builds.add_argument("--output-format", choices=["text", "json", "text-row"],

+                                help=output_format_help)

      parser_builds.set_defaults(func="action_list_builds")

  

      #########################################################
@@ -1341,6 +1477,8 @@ 

  

      parser_get_chroot = subparsers.add_parser("get-chroot", help="Get chroot of a project")

      parser_get_chroot.add_argument("coprchroot", help="Path to a project chroot as owner/project/chroot or project/chroot")

+     parser_get_chroot.add_argument("--output-format", default="json", choices=["text", "json", "text-row"],

+                                    help=output_format_help)

      parser_get_chroot.set_defaults(func="action_get_chroot")

  

      parser_list_chroots = subparsers.add_parser("list-chroots", help="List all currently available chroots.")
@@ -1464,6 +1602,8 @@ 

                                        help="Also display data related to the latest succeeded build for the package.")

      parser_list_packages.add_argument("--with-all-builds", action="store_true",

                                        help="Also display data related to the builds for the package.")

+     parser_list_packages.add_argument("--output-format", default="json", choices=["text", "json", "text-row"],

+                                       help=output_format_help)

      parser_list_packages.set_defaults(func="action_list_packages")

  

      # package names listing
@@ -1487,6 +1627,8 @@ 

                                      help="Also display data related to the latest succeeded build for each package.")

      parser_get_package.add_argument("--with-all-builds", action="store_true",

                                      help="Also display data related to the builds for each package.")

+     parser_get_package.add_argument("--output-format", default="json", choices=["text", "json", "text-row"],

+                                     help=output_format_help)

      parser_get_package.set_defaults(func="action_get_package")

  

      # package deletion
@@ -1519,7 +1661,8 @@ 

  

      # module building

      parser_build_module = subparsers.add_parser("build-module", help="Builds a given module in Copr")

-     parser_build_module.add_argument("copr", help="The copr repo to build module in. Can be just name of project or even in format owner/project.")

+     parser_build_module.add_argument("copr", help="The copr repo to build module in. Can be just name of project "

+                                                   "or even in format owner/project.")

      parser_build_module_mmd_source = parser_build_module.add_mutually_exclusive_group(required=True)

      parser_build_module_mmd_source.add_argument("--url", help="SCM with modulemd file in yaml format")

      parser_build_module_mmd_source.add_argument("--yaml", help="Path to modulemd file in yaml format")

file modified
+16 -3
@@ -447,13 +447,17 @@ 

  `copr-cli get-chroot coprchroot`

  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  

- usage: copr get-chroot [-h] coprchroot

+ usage: copr get-chroot [-h] coprchroot [--output-format {json, text, text-row}]

  

  Print info of the given chroot.

  

  coprchroot::  

  Path to a project chroot as owner/project/chroot or project/chroot

  

+ --output-format FORMAT::

+ Set the formatting style. We recommend using json, which prints the required data in json format.

+ The text format prints the required data in a column, one piece of information per line.

+ The text-row format prints all information separated by a space on a single line.

  

  PACKAGE ACTIONS

  ---------------
@@ -654,7 +658,7 @@ 

  `copr-cli list-packages [options]`

  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  usage: copr list-packages [-h] [--with-latest-build]

-                           [--with-latest-succeeded-build] [--with-all-builds]

+                           [--with-latest-succeeded-build] [--with-all-builds] [--output-format {json, text, text-row}]

                            project

  

  Lists all packages in the given project in json format.
@@ -668,6 +672,10 @@ 

  --with-all-builds::     

  Also display data related to the builds for each package.

  

+ --output-format FORMAT::

+ Set the formatting style. We recommend using json, which prints the required data in json format.

+ The text format prints the required data in a column, one piece of information per line.

+ The text-row format prints all information separated by a space on a single line.

  

  `copr-cli list-package-names [options]`

  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -679,11 +687,16 @@ 

  `copr-cli get-package [options]`

  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  usage: copr get-package [-h] --name PKGNAME [--with-latest-build]

-                         [--with-latest-succeeded-build] [--with-all-builds]

+                         [--with-latest-succeeded-build] [--with-all-builds] [--output-format {json, text, text-row}]

                          project

  

  Similar to list-packages but returns just a single package directly as json structure (not wrapped in a list).

  

+ --output-format FORMAT::

+ Set the formatting style. We recommend using json, which prints the required data in json format.

+ The text format prints the required data in a column, one piece of information per line.

+ The text-row format prints all information separated by a space on a single line.

+ 

  

  `copr-cli delete-package [options]`

  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

file modified
+26
@@ -479,6 +479,32 @@ 

      assert stdout == "Edit chroot operation was successful.\n"

  

  

+ @mock.patch('copr.v3.proxies.project_chroot.ProjectChrootProxy.get')

+ @mock.patch('copr_cli.main.config_from_file', return_value=mock_config)

+ def test_get_chroot_output_format_json(_config_from_file, project_chroot_proxy_get, capsys):

+     project_chroot_proxy_get.return_value = Munch(

+         projectname="foo",

+         additional_packages=[],

+         additional_repos=[],

+         comps_name="None",

+         delete_after_days="None",

+         isolation="None",

+         mock_chroot="fedora-20-x86_64",

+         ownername="None",

+         with_opts=[],

+         without_opts=[]

+     )

+     main.main(argv=[

+         "get-chroot", "foo/f20",

+     ])

+     stdout, stderr = capsys.readouterr()

+     project_chroot_proxy_get.assert_called_once()

+     json_values = json.loads(stdout)

+     assert stderr == ''

+     assert json_values["projectname"] == "foo"

+     assert json_values["mock_chroot"] == "fedora-20-x86_64"

+ 

+ 

  @mock.patch('copr.v3.proxies.project.ProjectProxy.add')

  @mock.patch('copr_cli.main.config_from_file', return_value=mock_config)

  def test_create_multilib_project(config_from_file, project_proxy_add, capsys):

Build succeeded.

Metadata Update from @msuchy:
- Request assigned

2 years ago

Thank you for the PR @schlupov,
I really like the feature.

I tested it and it seems to work fine and the output looks as
expected (except for having two blank lines after the list-packages
output, which is a bit weird, but I am really just nitpicking here).

Can you please add some tests for this feature? It's up to you whether
unit tests for copr-cli or some beaker test.

The one thing that I don't like about this PR is that the code will
get really messy once we add --output-format parameter to more
commands. We could either separate the formatted_print into smaller
functions for each command but ideally, if there isn't any blocker
that I don't see, I would like to add some abstraction.

IMHO the formatted_print could look likes this

 def formatted_print(self, data, fields=None, style="json", layout="rows")

The fields attribute would specify what fields we want to print for
each output dict, and layout (or any better name) attribute would
specify if we want to print the fields in columns or rows (which is
the difference between list-builds and list-packages), e.g.

769681 light succeeded
765268 light succeeded
765248 light succeeded

# vs

auto_rebuild: False
id: 227582
name: light
ownername: frostyx
projectname: light
...

The formattted_print function would be then called like this for
listing builds:

fields = ["id", "state"]
self.formatted_print(builds_list, fields=fields, style=args.output_format, layout="columns")

and for listing packages:

self.formatted_print(packages_with_builds, style=args.output_format, layout="rows")

In the case of builds, I omitted the source_package value because it is
taken from a nested dict and will require more work, than just
specifying the field name as a string. I see two possible ways to
solve it. Either

fields = [
    "id", 
    lambda b: b["source_package"]["name"], 
    "state",
]

or not having fields attribute at all and passing a function to
get the data from dict that we want to print, e.g.

values = lambda b: [b["id"], b["source_package"]["name"], b["state"]]
self.formatted_print(builds_list, values, style=args.output_format, layout="columns")

Personally, I like the first variant better because it is IMHO easier to
grasp. Also, I don't really care about the terminology, whatever I used as
variable names or values is just for demonstration purposes. But what do you
think about the general idea? Is it doable?

I like your suggestions, I will use some of your ideas :)

Build succeeded.

rebased onto 4d6e9c3bb0d5bfac1e653b72a0a63d2d33134821

2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

rebased onto 3e4740c0078bb71a248d0a4af87bc16b62eb8745

2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

I used Jakub's ideas and rewrote the formatted_print function. Pylint complains about the enumerate, which I want to have there, and then also about the long line, which is already there from some older commit, but I can fix it. My tests for epel 7 do not pass, it reports: AttributeError: 'module' object has no attribute 'JSONDecodeError'. I don't know why this problem is only on epel 7.

Here I think pylint is right, for i, field in enumerate(fields) would look nicer. But even better is IMO something like:
for field, desired_field in zip(fields, desired_fields):.

This is the line that newly goes beyond the 120-chars limit. The PyLint error list comparator (csdiff) has hard times comparing old and new code warnings in this PR because there are multiple similarly-looking errors like this. The only thing the comparator can guess is that there's one such error added in the PR, so it blames the last occurrence ....

My tests for epel 7 do not pass, it reports: AttributeError: 'module' object has no attribute 'JSONDecodeError'. I don't know why this problem is only on epel 7.

That's becase epel-7 has Python 2.7, and the json.decoder doesn't have that exception implemented.

It would be awesome to encapsulate this into a separate abstract class. You can see how
much this is needed by the weird if-else logic wrt json vs text output.

Consider that we could implement also other formats, like the awesome python3-tabulate
output (one example was in our old, now removed script).
Or YAML, or anything else.

It would be nice to have something like
printer = PrinterFactory(field_names, format), and then tooling like
printer.add_item(data) and printer.done(). Some of the formats would just
flush stdout right after add_item() call (e.g. text format) and some would
need to continue constructing new lines in memory before they are flushed out
(json and tables), where done() triggers the final flush.

I view this as a nice opportunity to design some nice code architecture :-)

rebased onto 8d45c9b893381de6d6d2ac8d962f72a5f58d186f

2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

rebased onto 37f7220fc9e4e03bc4ee11bad710bf178b306a15

2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

rebased onto 4821f5ed4b8c8d1ff1950ffc638a92000fb25007

2 years ago

PTAL, I will correct the pylint errors as soon as you agree to my implementation.

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

The Factory should provide a resource, say "Printer". So if you were able to provide Factory class it should have the interface like:
pinter = PrinterFactory(format="json").

The good thing is that the Text variant doesn't have to wait with printing - and it can be more streamlined (no need to hoard data in memory).

Since some Printers will print the data as "stream", I'd just call this metod like "finish", "finalize", "flush".

rebased onto de369f08b244b0d575899939a29265b48f5a8e77

2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

I changed the behavior of the classes, PTAL.

rebased onto 31874f04199cc048982fb7a79f8604f2fefdbe44

2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

Nice! This is promising :-) thank you. Please fix the test-suite and PyLint issues.

We need to take a look at compat-output test-case. Such that will succeed with
the old code, and also will succeed with the new output. Just to be sure that
we don't break user scripts. I can take a look at this task if you don't want to,
feel free to ping me.

rebased onto 0b26c64138d74cb2122172342237a3e36a5020a5

2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

rebased onto 90c0ab389cdbb5a1550bf8d1c133dc88f9cabc5c

2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

rebased onto 9e1a1079bd0c1e17287548e89277cd27fb0357aa

2 years ago

Feel free to ignore the super PyLint note, we need to stay compatible with python 2.7 on el7. We should add super-with-arguments to ignorelist in pylintrc.

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

But I can't say that I like the fact that we have different __init__ arguments in sub-classes ... which brings a bit of inconvenience.

Should this be just AbstractPrinter?

2 new commits added

  • cli: fix output format, new option --output-format
  • test: cli: Test for get-chroot command
2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

rebased onto d00727c59201d07e8ec0f30f72c89d355013a763

2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

It is quite hard to believe :-) but your patch causes some weird duplicity in the output ... with the system-wide client I can see each package only once in the output, but with this PR I can see dummy-pkg multiple times in ./copr list-packages praiskup/ping

Could we add a single-line separator between lines? Something like if not first: print() ?

When no explicit --output-format for copr list-builds is specified, we should throw a warning
that the default is going to be changed to from text-row to json in the following releases.

This could break the list() like output with only one field. People would still expect output like[{...}].

2 new commits added

  • cli: fix output format, new option --output-format
  • test: cli: Test for get-chroot command
2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

This could break the list() like output with only one field. People would still expect output like[{...}].

I follow how the output looked before this PR, get-chroot and get-package print {...}, not [{...}]

I follow how the output looked before this PR, get-chroot and get-package print {...}, not [{...}]

Definitely, but what if you do copr list-builds and there's only one build?

2 new commits added

  • cli: fix output format, new option --output-format
  • test: cli: Test for get-chroot command
2 years ago

I follow how the output looked before this PR, get-chroot and get-package print {...}, not [{...}]

Definitely, but what if you do copr list-builds and there's only one build?

At the moment I have solved it for list-builds, but in the future I would convert everything to the format [{...}]

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

This is indeed hard to understand. I'd rather see something less magic, or at least please properly document what those conditions mean :-).

2 new commits added

  • cli: fix output format, new option --output-format
  • test: cli: Test for get-chroot command
2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

This is indeed hard to understand. I'd rather see something less magic, or at least please properly document what those conditions mean :-).

Comment added :-)

I can not say I like this description (we should have a knob for different behavior) but I'm not going to delay the PR on this.

2 new commits added

  • cli: fix output format, new option --output-format
  • test: cli: Test for get-chroot command
2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

Please rebase (or even merge) #1744, and fix the rest of the pylint warnings.

rebased onto 059226e8d01952e389c4ee674ad07ade53630483

2 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

2 new commits added

  • cli: fix output format, new option --output-format
  • test: cli: Test for get-chroot command
2 years ago

Build succeeded.

In #1745 I added some tests, two of them seem to fail when I apply this PR.
That still needs to be fixed, and then I'm fine :-) thank you for your work here.

This is assigned to @frostyx, so I'll let the final review to him.

2 new commits added

  • cli: fix output format, new option --output-format
  • test: cli: Test for get-chroot command
2 years ago

Build succeeded.

2 new commits added

  • cli: fix output format, new option --output-format
  • test: cli: Test for get-chroot command
2 years ago

Build succeeded.

I am honestly surprised that pylint doesn't complain about this. Can you please define __init__ method for the AbstractPrinter so there is clear what arguments are expected?

No need to change since the parent class is abstract but generally I
think it is better to call the parent constructor as the first thing
and then continue doing the rest of the stuff. IMHO it avoids surprises
that something is not initialized yet/properly or that something gets
overwritten with the less-specific parent constructor. There are
exceptions to this for sure, sometimes the use-case requires the
parent constructor to be called the last ... but if not, I would
always start with it.

I just learned that the double underscore has a special meaning to the interpreter

Any identifier of the form __spam (at least two leading underscores,
at most one trailing underscore) is textually replaced with
_classname__spam, where classname is the current class name with
leading underscore(s) stripped. This mangling is done without regard
to the syntactic position of the identifier, so it can be used to
define class-private instance and class variables, methods, variables
stored in globals, and even variables stored in instances. private to
this class on instances of other classes.

If the use of double underscore was intentional, feel free to keep
it.

The field.__code__.co_varnames[0] would deserve some comment.

Also, the self.fields should be documented somewhere. Otherwise, it wouldn't be clear that it supports this callable functionality and that the callable field takes exactly one argument.

Same note to the double underscore as above

there is no other way to know that the list-builds command was used

I would create a subclass ListBuildsJsonPrinter that would inherit
JsonPrinter and redefine only the finish method. This would keep
this hack outside of an otherwise pretty abstraction.

The ListBuildsJsonPrinter would be then used only for the
list-builds command.

This test is good and thank you for that but I would really like to see some additional tests. I don't want to block this PR because of tests and they can be done in an additional PR, it's up to you.

Also, writing them is kinda tempting to me so if you don't want to write them yourself, feel free to pass this sub-task over.

We should test those classes in isolation and make sure add_data constructs the output properly. We should also test that lambda fields work as expected.

I guess that any mistakes would be caught by beaker tests but since we have a nice way to do it here, I wouldn't miss it.

Stupid me, I forgot this is already done in PR#1745

Metadata Update from @praiskup:
- Pull-request tagged with: needs-work

2 years ago

I just learned that the double underscore has a special meaning to the interpreter

Any identifier of the form __spam (at least two leading underscores,
at most one trailing underscore) is textually replaced with
_classname__spam, where classname is the current class name with
leading underscore(s) stripped. This mangling is done without regard
to the syntactic position of the identifier, so it can be used to
define class-private instance and class variables, methods, variables
stored in globals, and even variables stored in instances. private to
this class on instances of other classes.

If the use of double underscore was intentional, feel free to keep
it.

the double underscore is here on purpose, I want the variable __last to be set from the outside by setter set_last, there is no need to read it. When I do printer .__ last, pylint will complain.

rebased onto 5222b0698d4fda510eb1d86b805ebfe99f3cda3b

2 years ago

Metadata Update from @schlupov:
- Pull-request untagged with: needs-work

2 years ago

Build succeeded.

I'd also prefer if we could avoid playing with the __last and set_last. That just complicates the useability; callers should pay attention to anything else than constructor (fabric), finish() and add_data().

Can we do the last print() in finish() instead?

I like the code (I'll try to propose a follow-up PR for removing the lambda need), the only thing that I'd like to avoid in the initiall PR is the __last dance. Feel free to ping me on if you want/can discuss.

2 new commits added

  • cli: fix output format, new option --output-format
  • test: cli: Test for get-chroot command
2 years ago

Build succeeded.

Can you please rebase on top of the current main branch?

I don't complain here, as the lambda use, in this case, is rather a temporary solution. But as @frostyx mentioned, comment would be good.

But I like the PR, thank you.

rebased onto 669484e22adb3e203a5a3a867ee1db019ca9fb36

2 years ago

Rebased, the comment is in the AbstractPrinter class.

Build succeeded.

Yep, lets go for it :-)

rebased onto 741ae7a

2 years ago

Pull-Request has been merged by praiskup

2 years ago

Build succeeded.