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

Shorthand IPv4Network and IPv6Network #128810

Open
jfuruness opened this issue Jan 14, 2025 · 13 comments
Open

Shorthand IPv4Network and IPv6Network #128810

jfuruness opened this issue Jan 14, 2025 · 13 comments
Labels
stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@jfuruness
Copy link

jfuruness commented Jan 14, 2025

Feature or enhancement

Proposal:

IPv4Networks and IPv6Networks are often written in shorthand by network operators. For example, 1.2.0.0/16 would be written as 1.2/16. I've added a very simple .shorthand function (along with a few simple tests) to these networks in the hopes that this can be a supported feature.

For the IPv4Network:

    @property
    def shorthand(self):
        """
        Returns the shorthand representation of the IPv4 network.

        This method abbreviates the IPv4 network by removing trailing
        zero octets from the network address.

        Returns:
            str: The shorthand IPv4 network in the format 'X.X/X'.

        Example:
            >>> network = IPv4Network('192.168.0.0/24')
            >>> network.shorthand
            '192.168/24'
        """
        # Split the network address into octets
        octets = str(self.network_address).split('.')
        # Remove trailing zero octets
        while octets and octets[-1] == '0':
            octets.pop()
        # Rejoin the remaining octets and append the prefix length
        return '.'.join(octets) + f"/{self.prefixlen}"

For the IPv6Network:

    @property
    def shorthand(self):
        """
        Returns the shorthand representation of the IPv6 network.

        This method compresses the IPv6 address to its shortest form
        and appends the prefix length.

        Returns:
            str: The shorthand IPv6 network in the format 'X::/Y'.

        Example:
            >>> network = IPv6Network('2001:db8:0:0:0:0:0:0/32')
            >>> network.shorthand
            '2001:db8::/32'
        """
        return f"{self.network_address.compressed}/{self.prefixlen}"

Has this already been discussed elsewhere?

This is a minor feature, which does not need previous discussion elsewhere

Links to previous discussion of this feature:

No response

Linked PRs

@jfuruness
Copy link
Author

I'm a first time contributor so if I've missed a step please let me know! Thank you for your time.

@rruuaanng
Copy link
Contributor

            >>> network = IPv4Network('192.168.0.0/24')
            >>> network.shorthand
            '192.168/24'

The case you mentioned should be abbreviated as 192.168.0/24 because the mask is 24. Is it my misunderstanding of the abbreviation rule?

Typically, such behavior in network systems should follow the rules outlined below, if I remember correctly. In other words, the non-zero byte portions are retained based on the subnet mask.

192.0.0.0/8 => 192/8
192.1.0.0/16 => 192.1/16
192.168.1.0/24 => 192.168.1/24
192.168.0.0/24 => 192.168.0/24

@jfuruness
Copy link
Author

jfuruness commented Jan 14, 2025 via email

@picnixz picnixz added the stdlib Python modules in the Lib dir label Jan 14, 2025
@picnixz
Copy link
Member

picnixz commented Jan 14, 2025

Are "shorthand" IPv{4,6}Network standardized or is it just something that network operators came up with?

@ZeroIntensity
Copy link
Member

It's standard for IPv4, but I'm not sure about IPv6. The relevant RFC is RFC 4632. Though, I'm not really sure it's right to call it "shorthand"--it's really a notation for subnet masks, not necessarily a short version of the address. (Also, if we're going to support generating the notation from an IPv4Address object we should support parsing it into one as well.)

@jfuruness
Copy link
Author

jfuruness commented Jan 14, 2025 via email

@rruuaanng
Copy link
Contributor

To be honest, I like the term "shorthand", It perfectly captures this behavior, this notation is called CIDR, which can serve as an alternative name. Naming is a significant challenge in the field of computer science!

@picnixz
Copy link
Member

picnixz commented Jan 14, 2025

It's not the term "shorthand" that I'm worried about. What I'm worried about is whether this is a standardized notation and feature. If this is not, then we shouldn't include it in the standard library. Now, for IPv4 it seems that it is the case. If it's not the case for IPv6, I would prefer to amend the current PR.

@picnixz
Copy link
Member

picnixz commented Jan 14, 2025

Note that RFC 4632 is currently in "Best Current Practice" and not in the "Standard Tracks". Since BCP tracks may change in the future and evolve, it's not necessarily something that we want in the standard library. Is there a strong motivation to support shorthand notations (or a use case that cannot be achieved with standard notations)? Maybe we can instead add a comparision function that checks if two representations are equal instead. That way we could have more flexibility in the future.

@ZeroIntensity
Copy link
Member

Bikeshedding: cidr_shorthand is probably a better name, but I'd want good docs + docstring on that. (I'm all too familiar with having no idea what an acronym in the stdlib means.)

Google is saying that IPv6 supports CIDR notation as well, but yeah, being a "best practice" rather than a standard isn't convincing.

@ZeroIntensity
Copy link
Member

ZeroIntensity commented Jan 14, 2025

A thought I had: instead of explicitly adding cases for this, how about we add some sort of converter protocol for IP addresses? So something like:

class IPConverter:
    def parse_to_ipv4(self, text: str) -> IPv4Address:
        ...

    def convert_from_ipv4(self, address: IPv4Address) -> str:
        ...

    def parse_to_ipv6(self, text: str) -> IPv4Address:
        ...

    def convert_from_ipv6(self, address: IPv6Address) -> str:
        ...

ipaddress.add_converter(IPConverter())

Then, for this case, we could expose an optional CIDRConverter class that users could decide whether or not to use.

@picnixz
Copy link
Member

picnixz commented Jan 14, 2025

Then, for this case, we could expose an optional CIDRConverter class that users could decide whether or not to use.

How useful would it be if we only have one converter? I would prefer standalone functions rather than a namespace like this. OTOH, when I fixed ipaddress reverse pointers, I wanted to refactor the API as I thought some things could be improved here and there so maybe it could be a first step to that refactoring. That being said, this is just an idea and I would prefer a PoC to see first how it would be used and how it could improve the readability of the module in general.

@ZeroIntensity
Copy link
Member

How useful would it be if we only have one converter?

No idea, I'm thinking out loud. We could define a converter for every notation, and just keep the ones that currently exist enabled by default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

4 participants