Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create constants for top-level rolenames #1672

Merged
merged 2 commits into from
Dec 2, 2021

Conversation

ivanayov
Copy link
Collaborator

@ivanayov ivanayov commented Nov 12, 2021

Fixes #1648

This is a change in the metadata API to remove hardcoded role names
and use constants instead.

  • The code follows the Code Style Guidelines
  • Tests have been added for the bug fix or new feature - updated only
  • Docs have been added for the bug fix or new feature - n/a

@ivanayov ivanayov force-pushed the ivanayov/rolenames branch 2 times, most recently from 010dcc8 to 13d0345 Compare November 12, 2021 10:01
Copy link
Collaborator

@MVrachev MVrachev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution @ivanayov!
I have one preference about the way you defined your constants.
Still, I would want to hear an opinion from someone from the maintainers.
What do you think @sechkova?
Do you agree with my comment?

tuf/api/metadata.py Outdated Show resolved Hide resolved
@coveralls
Copy link

coveralls commented Nov 12, 2021

Pull Request Test Coverage Report for Build 1452565760

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 33 of 34 (97.06%) changed or added relevant lines in 3 files are covered.
  • 10 unchanged lines in 2 files lost coverage.
  • Overall coverage increased (+1.1%) to 98.599%

Changes Missing Coverage Covered Lines Changed/Added Lines %
tuf/ngclient/updater.py 12 13 92.31%
Files with Coverage Reduction New Missed Lines %
tuf/api/metadata.py 5 98.0%
tuf/ngclient/updater.py 5 95.07%
Totals Coverage Status
Change from base Build 1449719184: 1.1%
Covered Lines: 3626
Relevant Lines: 3645

💛 - Coveralls

@sechkova
Copy link
Contributor

I agree with both of you ...
On one hand, Root, Targets etc. classes already exist in metadata.py and now if we add similar constants for role names, we'll be importing things like

from tuf.api.metadata import Targets, TARGETS

On the other hand, names do grow in size.

I was thinking if it there is a convenient data structure than would:

  • encapsulate rolenames
  • act as iterator allowing us to call list(Rolenames); for role in Rolenames: and by doing this replaces TOP_LEVEL_ROLE_NAMES too
  • have a short enough name 🤷

But this is only a suggestion and if implementing this feels like an overkill for python-tuf, I'm ok with simple constants.

@MVrachev
Copy link
Collaborator

@lukpueh do you have an opinion on the matter?
I am asking you because we had discussed such issues before with you.

@lukpueh
Copy link
Member

lukpueh commented Nov 12, 2021

FWIW, in warehouse they use an enum subclass to do this, maybe that's the data structure @sechkova was thinking of, it seems to at least allow iterating etc.? :)

Another way of encapsulation would be to make them class attributes of the corresponding metadata classes.

I'm really fine either way.

@ivanayov
Copy link
Collaborator Author

I think enum covers all the cases, as @lukpueh also mentioned.

@ivanayov
Copy link
Collaborator Author

I pushed an update with enum.

Copy link
Collaborator

@MVrachev MVrachev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I am still uncertain about the usage of an enum here compared to using separate constants, and given that they will be uppercase I don't think they will be mistaken with something else.

Still, if we decide to use enums I have two suggestions:

  1. when defining RoleNames make sure you inherit str, this will shorten the comparisons as I explained in my comment
  2. when iterating all of the roles make sure to do: for role in RoleNames instead of mentioning each of the roles explicitly.
    I left a comment about that too.

EDIT: I think you can directly just inherit StrEnum with the same success as inheriting Enum and str separatly.
For more info: https://docs.python.org/3/library/enum.html#others

tests/repository_simulator.py Outdated Show resolved Hide resolved
tuf/api/metadata.py Outdated Show resolved Hide resolved
Copy link
Member

@jku jku left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have opinions on the actual data structure but I have a couple on the usage:

  • a few legacy test files are modified: The split between legacy and metadata API is confusing so not your fault, but let's not start mixing the two implementations. Basically if the file did not import tuf.api.metadata already, it's probably legacy and we don't want to modify the file. so test_repository_tool, test_sig, test_updater should not be touched
  • It's great that we try to avoid typos by avoiding hard-coded strings... but it should not come at at the expense of readability: I think going from "root" to RoleNames.ROOT.value is not a clear cut win as the latter is more than 3 times as long (!) and looks so complex -- I could not guess from the name that I'm looking at a simple role name.

@jku
Copy link
Member

jku commented Nov 15, 2021

It seems like the major reason to use an enum or a class is to avoid top level variable name pollution (so to avoid from tuf.api.metadata import Root, ROOT, Timestamp, TIMESTAMP, ....), correct?

What if instead of a new class we used the classes we are already importing? Root and others already have a class variable _signed_type which is already exposed as an instance property type... Maybe we could just simplify that so the class variable is called type (and is public): then we could just refer to Root.type to get the string without instantiating a Root?

(for reference: the class variable _signed_type is probably currently private to make it very clear it should not be modified, but as python does not have "class properties" that means we can't keep using a property in this case(?). I think solving the problem described in this issue is more useful than keeping the type private)

@lukpueh
Copy link
Member

lukpueh commented Nov 15, 2021

What if instead of a new class we used the classes we are already importing? Root and others already have a class variable _signed_type which is already exposed as an instance property type... Maybe we could just simplify that so the class variable is called type (and is public): then we could just refer to Root.type to get the string without instantiating a Root?

This is what I meant above with ...

Another way of encapsulation would be to make them class attributes of the corresponding metadata classes.

One downside would be that we'd have to define a global TOP_LEVEL_ROLE_NAMES that uses these class variables after the class definitions, i.e. at the bottom of the metadata module. But I guess that's okay.

@jku
Copy link
Member

jku commented Nov 15, 2021

One downside would be that we'd have to define a global TOP_LEVEL_ROLE_NAMES that uses these class variables after the class definitions, i.e. at the bottom of the metadata module.

I'm also not going to lose sleep over a single instance of duplicating the strings within the API implementation if that makes the API definition look neater: what's important is removing the magic strings from the code that imports tuf.api.metadata

@ivanayov
Copy link
Collaborator Author

I've put the constants within the role classes. Agree that it's more clear, simple and convenient.

As those are both required private (we don't want to be able to change them, whatever "be able" means in python for private properties :D ), but at the same time we need them for static reference within Updater, and we don't want to break the file format which uses _type, and don't want to do a breaking API change for such a simple reason with changing type to TYPE, we came to and agreement with @jku that changing _signed_type to type and keeping the _type property would be the best possible option.
Yep, it's still not very nice to reference the public variable from the private property, but that's the least painful and best to use change, unless someone comes with a genius idea that we cannot see yet.

@ivanayov ivanayov force-pushed the ivanayov/rolenames branch 2 times, most recently from 699972c to 1c0c253 Compare November 25, 2021 17:37
Copy link
Member

@jku jku left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, left a couple of comments but I think none were blockers: please take a look and adapt or don't (leave a comment if you don't), either way works

tests/test_trusted_metadata_set.py Outdated Show resolved Hide resolved
tests/test_updater_top_level_update.py Outdated Show resolved Hide resolved
tuf/api/metadata.py Outdated Show resolved Hide resolved
@jku
Copy link
Member

jku commented Nov 29, 2021

it's still not very nice to reference the public variable from the private property, but that's the least painful and best to use change, unless someone comes with a genius idea that we cannot see yet.

I think the genius idea is to just remove _type property: it's from the time when the API matching the file format was considered a bit more important than it is now. IMO the _type property is not worth the confusion it creates . But we can handle that in later PRs.

@lukpueh
Copy link
Member

lukpueh commented Nov 30, 2021

just remove _type property

Agreed!

it's from the time when the API matching the file format was considered a bit more important than it is now

I actually suspect that in this case the (Python) implementation influenced the naming decision in the spec, to avoid shadowing the type global on json-to-dict deserialisation.

Regardless of this specific case, I still think that it very important that the spec file format is recognisable in the reference implementation.

Ivana Atanasova added 2 commits December 2, 2021 12:16
This is a change in the metadata API to remove hardcoded rolenames
and use constants instead.
Fixes theupdateframework#1648

Signed-off-by: Ivana Atanasova <iyovcheva@iyovcheva-a02.vmware.com>
This applies the use of constants of top-level rolenames in the
tests instead of the previously hardcoded strings.
Fixes theupdateframework#1648

Signed-off-by: Ivana Atanasova <iyovcheva@iyovcheva-a02.vmware.com>
@ivanayov
Copy link
Collaborator Author

ivanayov commented Dec 2, 2021

Thanks @jku. Just addressed your comments.

@jku jku merged commit dd5deee into theupdateframework:develop Dec 2, 2021
@lukpueh lukpueh mentioned this pull request Dec 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

metadata API: Constants for top-level rolenames
6 participants