On Thu, Sep 5, 2024 at 7:56 AM Juraj Linkeš <juraj.lin...@pantheon.tech> wrote:
>
>
>
> On 26. 8. 2024 19:11, Jeremy Spewock wrote:
> > On Wed, Aug 21, 2024 at 10:53 AM Juraj Linkeš
> > <juraj.lin...@pantheon.tech> wrote:
> > <snip>
> >>   @dataclass
> >>   class TestPmdPort(TextParser):
> >>       """Dataclass representing the result of testpmd's ``show port info`` 
> >> command."""
> >> @@ -962,3 +1043,96 @@ def _close(self) -> None:
> >>           self.stop()
> >>           self.send_command("quit", "Bye...")
> >>           return super()._close()
> >> +
> >> +    """
> >> +    ====== Capability retrieval methods ======
> >> +    """
> >> +
> >> +    def get_capabilities_rxq_info(
> >> +        self,
> >> +        supported_capabilities: MutableSet["NicCapability"],
> >> +        unsupported_capabilities: MutableSet["NicCapability"],
> >> +    ) -> None:
> >> +        """Get all rxq capabilities and divide them into supported and 
> >> unsupported.
> >> +
> >> +        Args:
> >> +            supported_capabilities: Supported capabilities will be added 
> >> to this set.
> >> +            unsupported_capabilities: Unsupported capabilities will be 
> >> added to this set.
> >> +        """
> >> +        self._logger.debug("Getting rxq capabilities.")
> >> +        command = f"show rxq info {self.ports[0].id} 0"
> >> +        rxq_info = TestPmdRxqInfo.parse(self.send_command(command))
> >> +        if rxq_info.rx_scattered_packets:
> >> +            supported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED)
> >> +        else:
> >> +            
> >> unsupported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED)
> >> +
> >> +    """
> >> +    ====== Decorator methods ======
> >> +    """
> >> +
> >> +    @staticmethod
> >> +    def config_mtu_9000(testpmd_method: TestPmdShellSimpleMethod) -> 
> >> TestPmdShellDecoratedMethod:
> >
> > It might be more valuable for me to make a method for configuring the
> > MTU of all ports so that you don't have to do the loops yourself, I
> > can add this to the MTU patch once I update that and rebase it on
> > main.
> >
>
> Sure, if you add that, I'll use it here. :-)
> What won't work with that is the per-port restoration of MTU. But if we
> assume that MTU is always the same for all ports, then I don't think
> that's going to be a problem. This assumption doesn't seem unreasonable,
> I don't see a scenario where it would differ.

Good point, and something I didn't think about. I doubt they would be
different either though and I think it would generally be fine to
assume they are the same, but that could also be reason to do it on a
per-port basis. Whatever you think is best. Setting the MTU on all
ports isn't as efficient as I thought it would be when I first wrote
this comment anyway since testpmd doesn't offer something like a `port
config mtu all`, so I just do it one port at a time anyway.

>
> >> +        """Configure MTU to 9000 on all ports, run `testpmd_method`, then 
> >> revert.
> >> +
> >> +        Args:
> >> +            testpmd_method: The method to decorate.
> >> +
> >> +        Returns:
> >> +            The method decorated with setting and reverting MTU.
> >> +        """
> >> +
<snip>
> >> +    @classmethod
> >> +    def get_unique(
> >> +        cls, nic_capability: NicCapability, decorator_fn: 
> >> TestPmdShellDecorator | None
> >> +    ) -> "DecoratedNicCapability":
> >
> > This idea of get_unique really confused me at first. After reading
> > different parts of the code to learn how it is being used, I think I
> > understand now what it's for. My current understanding is basically
> > that you're using an uninstantiated class as essentially a factory
> > that stores a dictionary that you are using to hold singletons.
>
> Just a note, these are not singletons, just similar to them. A singleton
> is just one instance of class can exist. This class allows more
> instances, but it does limit the instances. It closer to an Enum, which
> work exactly the same way, but only attribute names are taken into
> consideration (with Enums).

That's a good distinction to make. Singleton was the closest thing
that I could make the connection too, but you're right that it isn't
the same and the comparison to Enums makes a lot of sense.

>
> > It
> > might be confusing to me in general because I haven't really seen this
> > idea of dynamically modifying attributes of a class itself rather than
> > an instance of the class used this way. Understanding it now, it makes
> > sense what you are trying to do and how this is essentially a nice
> > cache/factory for singleton values for each capability, but It might
> > be helpful to document a little more somehow that _unique_capabilities
> > is really just a container for the singleton capabilities, and that
> > the top-level class is modified to keep a consistent state throughout
> > the framework.
> >
> > Again, it could just be me having not really seen this idea used
> > before, but it was strange to wrap my head around at first since I'm
> > more used to class methods being used to read the state of attributes.
> >
>
> I'm thinking of adding this to get_unique's docstring:
>
> This is a factory method that implements a quasi-enum pattern.
> The instances of this class are stored in a class variable,
> _unique_capabilities.
>
> If an instance with `nic_capability` and `decorator_fn` as inputs
> doesn't exist, it is created and added to _unique_capabilities.
> If it exists, it is returned so that a new identical instance is not
> created.

Sure, I think this reads pretty well, and I like specifically calling
out the pattern so that if anyone was unfamiliar it gives them
something to research.

>
>
> >> +        """Get the capability uniquely identified by `nic_capability` and 
> >> `decorator_fn`.
> >> +
> >> +        Args:
> >> +            nic_capability: The NIC capability.
> >> +            decorator_fn: The function that will be passed the function 
> >> associated
> >> +                with `nic_capability` when discovering the support status 
> >> of the capability.
> >> +
> >> +        Returns:
> >> +            The capability uniquely identified by `nic_capability` and 
> >> `decorator_fn`.
> >> +        """
<snip>
> >> +    @classmethod
> >> +    def _reduce_capabilities(
> >> +        cls,
> >> +        capabilities: set["DecoratedNicCapability"],
> >> +        supported_capabilities: MutableSet,
> >> +        unsupported_capabilities: MutableSet,
> >> +    ) -> TestPmdShellSimpleMethod:
> >> +        def reduced_fn(testpmd_shell: TestPmdShell) -> None:
> >> +            for capability in capabilities:
>
> This is where I'll add the fix:
> if capability not in supported_capabilities | unsupported_capabilities:
>

Perfect, I think that would solve it, yes.

> >> +                capability.nic_capability(
> >> +                    testpmd_shell, supported_capabilities, 
> >> unsupported_capabilities
> >> +                )
> >> +
> >> +        return reduced_fn
> >
> > Would it make sense to put these two methods above
> > get_supported_capabilities since that is where they are used? I might
> > be in favor of it just because it would save you from having to look
> > further down in the diff to find what the method does and then go back
> > up, but I also understand that it looks like you might have been
> > sorting methods by private vs. public so if you think it makes more
> > sense to leave them here that is also viable.
> >
>
> I sorted it this what so that the code it's easier to read (in my
> opinion). I read the method, what it does, then the method calls a
> method I haven't seen so I go look beneath the method for the method
> definition. To me, this is preferable that reading methods I haven't
> seen before. Or, put in another way, the methods are sorted in the order
> they're used in code (that's how the code is executed and that's why
> this order feels natural to me).

Right, that does also make sense and is more accurate to how the code
runs. I think it is fine to leave this way then.

>
> >> +
> >> +    def __hash__(self) -> int:
> >> +        """Instances are identified by :attr:`nic_capability` and 
> >> :attr:`capability_decorator`."""
> >> +        return hash((self.nic_capability, self.capability_decorator))
> >
> > I guess my question above is asking if `hash(self.nic_capability) ==
> > hash(self.nic_capability.value())` because, if they aren't, then I
> > think the map will contain multiple capabilities that use the same
> > testpmd function since the capabilities themselves are unique, and
> > then because the get_supported_capabilities() method above just calls
> > whatever is in this map, it would call it twice. I think the whole
> > point of the NoAliasEnum is making sure that they don't hash to the
> > same thing. I could be missing something, but, if I am, maybe some
> > kind of comment showing where this is handled would be helpful.
> >
>
> I think the simple fix in _reduce_capabilities() addresses this, right?

Yes it does, and it does so better than if the two hashes were equal anyway.

>
> >> +
<snip>
>

Reply via email to