diff --git a/construction/index.html b/construction/index.html index 529ed5a..0f160b5 100644 --- a/construction/index.html +++ b/construction/index.html @@ -512,98 +512,94 @@

Constructing Range and Ranges

  • explicitly, like you would create a range or slice
  • Range from a string

    -
    from arranges import Ranges
    -
    -# Create ranges from a string. Like a slice but without the square brackets.
    -
    -the_full_range = Ranges(":")  # an endless sequence starting at 0
    -the_empty_range = Ranges("")  # start, stop and len() are all 0
    -from_0_to_99 = Ranges("0:100")
    -skip_10_items = Ranges("10:")  # boundless, goes to infinity
    -access_by_index = Ranges("0")  # just the first item
    -
    -# Decimal, hexadecimal, octal and binary are supported. Note that octal uses
    -# Python's 0o prefix, not 0 like in C.
    -gbc_palette = Ranges("0xff68:0xff88")
    -skip_bmp_header = Ranges("0o66:end")  # skip "54", "0x36" or "0b1100110" bytes
    -
    -# "end" and "inf" are the same. You can also use "start" instead of 0.
    -the_full_range_again = Ranges("start:inf")
    -
    -# whitespace is ignored during construction
    -first_kilobyte = Ranges("start : 0x400")
    -
    -# They are simplified when converted to str, which they can be compared with.
    -assert first_kilobyte == ":1024"
    -
    -# Comparisons work in both directions
    -assert "000001:000002" == Ranges("1")
    -
    -# and the hash is the same as the string
    -w = Ranges("6 : 8")
    -assert hash(w) == hash("6:8")
    -
    -# so you can use them as dict keys
    -d = {"6:8": "width"}
    -d[w] = "GIF file width"
    -assert d == {"6:8": "GIF file width"}
    -
    - +
    from arranges import Ranges
    +
    +# Create ranges from a string. Like a slice but without the square brackets.
    +
    +the_full_range = Ranges(":")  # an endless sequence starting at 0
    +the_empty_range = Ranges("")  # start, stop and len() are all 0
    +from_0_to_99 = Ranges("0:100")
    +skip_10_items = Ranges("10:")  # boundless, goes to infinity
    +access_by_index = Ranges("0")  # just the first item
    +
    +# Decimal, hexadecimal, octal and binary are supported. Note that octal uses
    +# Python's 0o prefix, not 0 like in C.
    +gbc_palette = Ranges("0xff68:0xff88")
    +skip_bmp_header = Ranges("0o66:end")  # skip "54", "0x36" or "0b1100110" bytes
    +
    +# "end" and "inf" are the same. You can also use "start" instead of 0.
    +the_full_range_again = Ranges("start:inf")
    +
    +# whitespace is ignored during construction
    +first_kilobyte = Ranges("start : 0x400")
    +
    +# They are simplified when converted to str, which they can be compared with.
    +assert first_kilobyte == ":1024"
    +
    +# Comparisons work in both directions
    +assert "000001:000002" == Ranges("1")
    +
    +# and the hash is the same as the string
    +w = Ranges("6 : 8")
    +assert hash(w) == hash("6:8")
    +
    +# so you can use them as dict keys
    +d = {"6:8": "width"}
    +d[w] = "GIF file width"
    +assert d == {"6:8": "GIF file width"}
    +

    Ranges from a string

    -
    from arranges import Ranges
    -
    -# You can put gaps in your range using commas.
    -pages_2_to_5_and_20 = Ranges("2:6, 20")
    -
    -# The ranges are sorted and combined as they are added
    -overlapping = Ranges("start:10, 14, 1:3")
    -assert overlapping == ":10,14"
    -
    -# But you can't hash Ranges because they're mutable
    -import pytest
    -
    -with pytest.raises(ValueError):
    -    a = {overlapping: "whatever"}
    -
    - +
    from arranges import Ranges
    +
    +# You can put gaps in your range using commas.
    +pages_2_to_5_and_20 = Ranges("2:6, 20")
    +
    +# The ranges are sorted and combined as they are added
    +overlapping = Ranges("start:10, 14, 1:3")
    +assert overlapping == ":10,14"
    +
    +# But you can't hash Ranges because they're mutable
    +import pytest
    +
    +with pytest.raises(ValueError):
    +    a = {overlapping: "whatever"}
    +

    Ranges from range-like objects

    If it has a start, stop and step attribute then it quacks like a range so you can pass it in as the value. If the step value is something other than None or 1 then it'll raise ValueError.

    -
    from arranges import Ranges
    -
    -from_slice = Ranges(slice(10, 20))
    -from_range = Ranges(range(10, 20))
    -from_other = Ranges(Ranges("10:20"))
    -
    -assert from_slice == from_range == from_other == "10:20"
    -
    - +
    from arranges import Ranges
    +
    +from_slice = Ranges(slice(10, 20))
    +from_range = Ranges(range(10, 20))
    +from_other = Ranges(Ranges("10:20"))
    +
    +assert from_slice == from_range == from_other == "10:20"
    +

    Ranges from sequences

    -
    from arranges import Ranges
    -
    -l = list(range(100))
    -t = tuple(range(100))
    -i = (i for i in range(100))
    -
    -assert Ranges(l) == Ranges(t) == Ranges(i) == ":100"
    -
    -jumble = Ranges([0, [2, 4], ["1:3"], 3, 4, range(10, 20)])
    -
    -assert jumble == "5,10:20"
    -
    - +
    from arranges import Ranges
    +
    +l = list(range(100))
    +t = tuple(range(100))
    +i = (i for i in range(100))
    +
    +assert Ranges(l) == Ranges(t) == Ranges(i) == ":100"
    +
    +jumble = Ranges([0, [2, 4], ["1:3"], 3, 4, range(10, 20)])
    +
    +assert jumble == "5,10:20"
    +

    Explicitly creating Ranges

    You can create them in the same way as range or slice objects which have a slightly different syntax to slice notation.

    -
    from arranges import Ranges, inf
    -
    -assert Ranges(10, inf) == "10:"
    -
    -# Ranges("10") is not the same as Ranges(10)
    -assert Ranges(10) == ":10"
    -assert Ranges("10") == 10 == Ranges(10, 11)
    -
    +
    from arranges import Ranges, inf
    +
    +assert Ranges(10, inf) == "10:"
    +
    +# Ranges("10") is not the same as Ranges(10)
    +assert Ranges(10) == ":10"
    +assert Ranges("10") == 10 == Ranges(10, 11)
    +
    diff --git a/iteration/index.html b/iteration/index.html index 63e6bf1..d62d31a 100644 --- a/iteration/index.html +++ b/iteration/index.html @@ -463,11 +463,10 @@

    Iteration

    Ranges objects are sequences of integers that you can iterate over.

    -
    from arranges import Ranges
    -
    -assert list(Ranges("start:3, 10")) == [0, 1, 2, 10]
    -
    - +
    from arranges import Ranges
    +
    +assert list(Ranges("start:3, 10")) == [0, 1, 2, 10]
    +

    Cardinality of the address range

    The length of a range is the number of elements it contains. But because ranges can be boundless, we have a special inf value that represents an infinite int @@ -475,36 +474,35 @@

    Cardinality of the address range

    This value is math.inf, but when returned from len it becomes sys.maxsize as it only supports ints that are this size or smaller. When comparing inf to sys.maxsize it'll return True, but this doesn't work in all directions:

    -
    import math
    -import sys
    -from arranges import Ranges, inf
    -
    -assert len(Ranges("1,2,3,4,10:20")) == 14
    -
    -full = Ranges(":")
    -
    -assert len(full) == sys.maxsize
    -assert len(full) == inf
    -assert sys.maxsize == inf
    -assert full.stop == inf
    -assert inf - 100 == sys.maxsize
    -
    -# Careful though, this does not hold
    -assert len(full) != math.inf
    -
    -# because it's an int
    -assert type(len(full)) is int
    -
    - +
    import math
    +import sys
    +from arranges import Ranges, inf
    +
    +assert len(Ranges("1,2,3,4,10:20")) == 14
    +
    +full = Ranges(":")
    +
    +assert len(full) == sys.maxsize
    +assert len(full) == inf
    +assert sys.maxsize == inf
    +assert full.stop == inf
    +assert inf - 100 == sys.maxsize
    +
    +# Careful though, this does not hold
    +assert len(full) != math.inf
    +
    +# because it's an int
    +assert type(len(full)) is int
    +

    Truthyness

    Empty ranges are, of course, Falsey.

    -
    from arranges import Ranges
    -
    -assert Ranges(":")
    -assert Range(10)
    -
    -assert not Ranges("")
    -
    +
    from arranges import Ranges
    +
    +assert Ranges(":")
    +assert Range(10)
    +
    +assert not Ranges("")
    +
    diff --git a/models/index.html b/models/index.html index 23cc4d8..402c91d 100644 --- a/models/index.html +++ b/models/index.html @@ -397,24 +397,24 @@

    Use in Pydantic models

    You'll need Pydantic >=2.3:

    -
    from pydantic import BaseModel, Field
    -
    -
    -class BinaryPatch(BaseModel):
    -    """
    -
    -    """
    -    regions: Ranges
    -
    -
    -model = BinaryPatch.model_validate_json("""
    -{
    -    "regions": "0:10, 20:30, 15:, 0x0b"
    -}
    -""")
    -
    -assert model.regions == ":10,15:"
    -
    +
    from pydantic import BaseModel, Field
    +
    +
    +class BinaryPatch(BaseModel):
    +    """
    +
    +    """
    +    regions: Ranges
    +
    +
    +model = BinaryPatch.model_validate_json("""
    +{
    +    "regions": "0:10, 20:30, 15:, 0x0b"
    +}
    +""")
    +
    +assert model.regions == ":10,15:"
    +
    diff --git a/pydoc/index.html b/pydoc/index.html index efab0b3..5537535 100644 --- a/pydoc/index.html +++ b/pydoc/index.html @@ -399,146 +399,125 @@

    arranges

    arranges.ranges

    Ranges Objects

    -
    class Ranges(str)
    -
    - +
    class Ranges(str)
    +

    A range set that can be hashed and converted to a string.

    __init__

    -
    def __init__(value: Any, stop: range_idx | None = None)
    -
    - +
    def __init__(value: Any, stop: range_idx | None = None)
    +

    Construct a new string with the canonical form of the range.

    __new__

    -
    def __new__(cls, value: Any, stop: range_idx | None = None) -> str
    -
    - +
    def __new__(cls, value: Any, stop: range_idx | None = None) -> str
    +

    Construct a new string with the canonical form of the range.

    This becomes "self" in init, so we're always a string

    construct_str

    -
    @classmethod
    -def construct_str(cls, value, stop) -> str
    -
    - +
    @classmethod
    +def construct_str(cls, value, stop) -> str
    +

    Create a string representation of a range series

    from_str

    -
    @classmethod
    -def from_str(cls, value: str) -> tuple[Segment]
    -
    - +
    @classmethod
    +def from_str(cls, value: str) -> tuple[Segment]
    +

    Construct from a string.

    iterable_to_str

    -
    @classmethod
    -def iterable_to_str(cls, iterable: Iterable) -> str
    -
    - +
    @classmethod
    +def iterable_to_str(cls, iterable: Iterable) -> str
    +

    Convert an iterable of ranges to a string

    from_hashable_iterable

    -
    @classmethod
    -@lru_cache
    -def from_hashable_iterable(cls, value: tuple[Any]) -> tuple[Segment]
    -
    - +
    @classmethod
    +@lru_cache
    +def from_hashable_iterable(cls, value: tuple[Any]) -> tuple[Segment]
    +

    Cache the result of from_iterable

    from_iterable

    -
    @classmethod
    -def from_iterable(cls, iterable: Iterable) -> tuple[Segment]
    -
    - +
    @classmethod
    +def from_iterable(cls, iterable: Iterable) -> tuple[Segment]
    +

    Sort and merge a list of ranges.

    __eq__

    -
    def __eq__(other: Any) -> bool
    -
    - +
    def __eq__(other: Any) -> bool
    +

    Compare the two lists based on their string representations

    __contains__

    -
    def __contains__(other: Any) -> bool
    -
    - +
    def __contains__(other: Any) -> bool
    +

    Are all of the other ranges in our ranges?

    __iter__

    -
    def __iter__()
    -
    - +
    def __iter__()
    +

    Iterate over the values in our ranges.

    Note that this could be boundless.

    intersects

    -
    def intersects(other: Any) -> bool
    -
    - +
    def intersects(other: Any) -> bool
    +

    True if this range overlaps with the other range

    union

    -
    def union(other) -> "Ranges"
    -
    - +
    def union(other) -> "Ranges"
    +

    Return the union of this range and the other

    __or__

    -
    def __or__(other: "Ranges") -> "Ranges"
    -
    - +
    def __or__(other: "Ranges") -> "Ranges"
    +

    Return the union of this range and the other

    __and__

    -
    def __and__(other: "Ranges") -> "Ranges"
    -
    - +
    def __and__(other: "Ranges") -> "Ranges"
    +

    Return the intersection of this range and the other

    __invert__

    -
    def __invert__()
    -
    - +
    def __invert__()
    +

    The inverse of this range

    validate

    -
    @classmethod
    -def validate(cls, value: Any) -> "Ranges"
    -
    - +
    @classmethod
    +def validate(cls, value: Any) -> "Ranges"
    +

    Validate a value and convert it to a Range

    __get_pydantic_core_schema__

    -
    @classmethod
    -def __get_pydantic_core_schema__(cls, source_type: Any,
    -                                 handler: GetCoreSchemaHandler) -> CoreSchema
    -
    - +
    @classmethod
    +def __get_pydantic_core_schema__(cls, source_type: Any,
    +                                 handler: GetCoreSchemaHandler) -> CoreSchema
    +

    For automatic validation in pydantic

    arranges.utils

    Some quacky type helpers so we don't have to use isinstance everywhere

    _Boundless Objects

    -
    class _Boundless(float)
    -
    - +
    class _Boundless(float)
    +

    A class that represents a boundless end of a range

    huge

    An enormous number that's used as a stand-in for infinity

    __eq__

    -
    def __eq__(other) -> bool
    -
    - +
    def __eq__(other) -> bool
    +

    Is this boundless?

    __index__

    -
    def __index__() -> int
    -
    - +
    def __index__() -> int
    +

    When used as an index, return a huge integer rather than infinity.

    This is necessary because CPython doesn't allow lengths larger than sys.maxsize, and Python has no way to represent infinity as an integer.

    @@ -548,150 +527,127 @@

    inf

    used as a length it's the largest index integer possible in cpython.

    to_int

    -
    def to_int(value: str, default: int) -> int
    -
    - +
    def to_int(value: str, default: int) -> int
    +

    Convert a string to an integer. If the string is empty, return the default

    is_rangelike

    -
    def is_rangelike(obj: Any) -> bool
    -
    - +
    def is_rangelike(obj: Any) -> bool
    +

    Check if a value is a range-like object

    is_intlike

    -
    def is_intlike(value: Any) -> bool
    -
    - +
    def is_intlike(value: Any) -> bool
    +

    Can this object be converted to an integer?

    is_iterable

    -
    def is_iterable(value: Any) -> bool
    -
    - +
    def is_iterable(value: Any) -> bool
    +

    Is this object iterable?

    as_type

    -
    def as_type(cls: Type[T], value: Any) -> T
    -
    - +
    def as_type(cls: Type[T], value: Any) -> T
    +

    Convert a value to a type, if necessary.

    Saves a bit of construction time if the value is already the right type.

    try_hash

    -
    def try_hash(obj: Any) -> int | None
    -
    - +
    def try_hash(obj: Any) -> int | None
    +

    Try to hash an object. If it can't be hashed, return None

    arranges.segment

    start_stop_to_str

    -
    def start_stop_to_str(start: range_idx, stop: range_idx) -> str
    -
    - +
    def start_stop_to_str(start: range_idx, stop: range_idx) -> str
    +

    Returns a string representation of a range from start to stop.

    Segment Objects

    -
    class Segment(str)
    -
    - +
    class Segment(str)
    +

    A single range segment that's a string and can be hashed.

    __init__

    -
    def __init__(start: range_idx, stop: range_idx = None)
    -
    - +
    def __init__(start: range_idx, stop: range_idx = None)
    +

    Construct a new string with the canonical form of the range.

    __new__

    -
    def __new__(cls, start: range_idx, stop: range_idx = None) -> str
    -
    - +
    def __new__(cls, start: range_idx, stop: range_idx = None) -> str
    +

    Construct a new string with the canonical form of the range.

    __or__

    -
    def __or__(other: "Segment") -> "Segment"
    -
    - +
    def __or__(other: "Segment") -> "Segment"
    +

    Return the union of this range and the other range

    __iter__

    -
    def __iter__()
    -
    - +
    def __iter__()
    +

    Iterate over the values in this range

    __len__

    -
    def __len__() -> int
    -
    - +
    def __len__() -> int
    +

    Get the length of this range

    __bool__

    -
    def __bool__() -> bool
    -
    - +
    def __bool__() -> bool
    +

    True if this range has a length

    last

    -
    @property
    -def last() -> int
    -
    - +
    @property
    +def last() -> int
    +

    Gets the last value in this range. Will return inf if the range has no end, and -1 if it has no contents,

    from_str

    -
    @classmethod
    -@lru_cache
    -def from_str(cls, value: str) -> "Segment"
    -
    - +
    @classmethod
    +@lru_cache
    +def from_str(cls, value: str) -> "Segment"
    +

    Construct from a string.

    sort_key

    -
    @staticmethod
    -def sort_key(value: "Segment") -> tuple[int, int]
    -
    - +
    @staticmethod
    +def sort_key(value: "Segment") -> tuple[int, int]
    +

    Sort key function for sorting ranges

    isdisjoint

    -
    def isdisjoint(other: Any) -> bool
    -
    - +
    def isdisjoint(other: Any) -> bool
    +

    Return True if this range is disjoint from the other range

    __eq__

    -
    def __eq__(other: Any) -> bool
    -
    - +
    def __eq__(other: Any) -> bool
    +

    Compare two segments

    isconnected

    -
    def isconnected(other: "Segment") -> bool
    -
    - +
    def isconnected(other: "Segment") -> bool
    +

    True if this range is adjacent to or overlaps the other range, and so they can be joined together.

    isadjacent

    -
    def isadjacent(other: "Segment") -> bool
    -
    - +
    def isadjacent(other: "Segment") -> bool
    +

    True if this range is adjacent to the other range

    intersects

    -
    def intersects(other: "Segment") -> bool
    -
    - +
    def intersects(other: "Segment") -> bool
    +

    True if this range intersects the other range.

    __contains__

    -
    def __contains__(other: Any) -> bool
    -
    - +
    def __contains__(other: Any) -> bool
    +

    Membership test. Supports integers, strings, ranges and iterables.

    diff --git a/search/search_index.json b/search/search_index.json index 29f009c..fe76173 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Arranges","text":""},{"location":"#range-string-fields-for-pydantic-basemodels","title":"Range string fields for Pydantic BaseModels","text":"

    I needed a way to parse batches of byte, row and line and other object ranges in my merge-files app, in a way that I can just drop it in as a string field type. The reason for this is so the machine-generated command line help is flat and readable by humans.

    It it kinda grew into a monster so I've split it out into this separate package. The main feature is a pair of classes that can represent ranges:

    The range class is designed to be used as fields in Pydantic BaseModels, but can be used anywhere you need a range. They are not designed with speed in mind, and comparisons usually use the canonical string form by converting other things into Ranges objects. Their preferred pronoun is they/them.

    "},{"location":"#constraints","title":"Constraints","text":"

    I made them to select lines or bytes in a stream of data, so they:

    "},{"location":"#installation","title":"Installation","text":"

    pip install arranges if you want to use them. You'll need Python 3.10 or above.

    "},{"location":"#dev-setup","title":"Dev setup","text":"

    To add features etc you'll ideally need git, make, bash and something with a debugger. Config for Visual Studio Code is included.

    Clone the repo and make dev to make the venv, install dependencies, then code . to open the project up in the venv with tests and debugging and all that jazz.

    Type make help to see the other options, or run the one-liner scripts in the ./build dir if you want to run steps without all that fancy caching nonsense.

    "},{"location":"#usage","title":"Usage","text":""},{"location":"#license","title":"License","text":"

    Free as in freedom from legalese; the WTFPL with a warranty clause.

    Political note: I don't want to live in a world where lawyers tell me how to speak. If you don't trust me enough to use the WTFPL then you shouldn't be running my code in the first place.

    "},{"location":"LICENSE/","title":"License","text":"

    Licensed under the WTFPL with one additional clause:

    1. Don't blame me.

    Do whatever the fuck you want, just don't blame me.

    "},{"location":"construction/","title":"Constructing Range and Ranges","text":"

    You can create them from:

    1. strings
    2. range-like objects
    3. sequences of ints or these things, or other sequences
    4. explicitly, like you would create a range or slice
    "},{"location":"construction/#range-from-a-string","title":"Range from a string","text":"
    from arranges import Ranges\n\n# Create ranges from a string. Like a slice but without the square brackets.\n\nthe_full_range = Ranges(\":\")  # an endless sequence starting at 0\nthe_empty_range = Ranges(\"\")  # start, stop and len() are all 0\nfrom_0_to_99 = Ranges(\"0:100\")\nskip_10_items = Ranges(\"10:\")  # boundless, goes to infinity\naccess_by_index = Ranges(\"0\")  # just the first item\n\n# Decimal, hexadecimal, octal and binary are supported. Note that octal uses\n# Python's 0o prefix, not 0 like in C.\ngbc_palette = Ranges(\"0xff68:0xff88\")\nskip_bmp_header = Ranges(\"0o66:end\")  # skip \"54\", \"0x36\" or \"0b1100110\" bytes\n\n# \"end\" and \"inf\" are the same. You can also use \"start\" instead of 0.\nthe_full_range_again = Ranges(\"start:inf\")\n\n# whitespace is ignored during construction\nfirst_kilobyte = Ranges(\"start : 0x400\")\n\n# They are simplified when converted to str, which they can be compared with.\nassert first_kilobyte == \":1024\"\n\n# Comparisons work in both directions\nassert \"000001:000002\" == Ranges(\"1\")\n\n# and the hash is the same as the string\nw = Ranges(\"6 : 8\")\nassert hash(w) == hash(\"6:8\")\n\n# so you can use them as dict keys\nd = {\"6:8\": \"width\"}\nd[w] = \"GIF file width\"\nassert d == {\"6:8\": \"GIF file width\"}\n
    "},{"location":"construction/#ranges-from-a-string","title":"Ranges from a string","text":"
    from arranges import Ranges\n\n# You can put gaps in your range using commas.\npages_2_to_5_and_20 = Ranges(\"2:6, 20\")\n\n# The ranges are sorted and combined as they are added\noverlapping = Ranges(\"start:10, 14, 1:3\")\nassert overlapping == \":10,14\"\n\n# But you can't hash Ranges because they're mutable\nimport pytest\n\nwith pytest.raises(ValueError):\n    a = {overlapping: \"whatever\"}\n
    "},{"location":"construction/#ranges-from-range-like-objects","title":"Ranges from range-like objects","text":"

    If it has a start, stop and step attribute then it quacks like a range so you can pass it in as the value. If the step value is something other than None or 1 then it'll raise ValueError.

    from arranges import Ranges\n\nfrom_slice = Ranges(slice(10, 20))\nfrom_range = Ranges(range(10, 20))\nfrom_other = Ranges(Ranges(\"10:20\"))\n\nassert from_slice == from_range == from_other == \"10:20\"\n
    "},{"location":"construction/#ranges-from-sequences","title":"Ranges from sequences","text":"
    from arranges import Ranges\n\nl = list(range(100))\nt = tuple(range(100))\ni = (i for i in range(100))\n\nassert Ranges(l) == Ranges(t) == Ranges(i) == \":100\"\n\njumble = Ranges([0, [2, 4], [\"1:3\"], 3, 4, range(10, 20)])\n\nassert jumble == \"5,10:20\"\n
    "},{"location":"construction/#explicitly-creating-ranges","title":"Explicitly creating Ranges","text":"

    You can create them in the same way as range or slice objects which have a slightly different syntax to slice notation.

    from arranges import Ranges, inf\n\nassert Ranges(10, inf) == \"10:\"\n\n# Ranges(\"10\") is not the same as Ranges(10)\nassert Ranges(10) == \":10\"\nassert Ranges(\"10\") == 10 == Ranges(10, 11)\n
    "},{"location":"iteration/","title":"Iteration","text":"

    Ranges objects are sequences of integers that you can iterate over.

    from arranges import Ranges\n\nassert list(Ranges(\"start:3, 10\")) == [0, 1, 2, 10]\n
    "},{"location":"iteration/#cardinality-of-the-address-range","title":"Cardinality of the address range","text":"

    The length of a range is the number of elements it contains. But because ranges can be boundless, we have a special inf value that represents an infinite int as Python doesn't have or support one.

    This value is math.inf, but when returned from len it becomes sys.maxsize as it only supports ints that are this size or smaller. When comparing inf to sys.maxsize it'll return True, but this doesn't work in all directions:

    import math\nimport sys\nfrom arranges import Ranges, inf\n\nassert len(Ranges(\"1,2,3,4,10:20\")) == 14\n\nfull = Ranges(\":\")\n\nassert len(full) == sys.maxsize\nassert len(full) == inf\nassert sys.maxsize == inf\nassert full.stop == inf\nassert inf - 100 == sys.maxsize\n\n# Careful though, this does not hold\nassert len(full) != math.inf\n\n# because it's an int\nassert type(len(full)) is int\n
    "},{"location":"iteration/#truthyness","title":"Truthyness","text":"

    Empty ranges are, of course, Falsey.

    from arranges import Ranges\n\nassert Ranges(\":\")\nassert Range(10)\n\nassert not Ranges(\"\")\n
    "},{"location":"models/","title":"Use in Pydantic models","text":"

    You'll need Pydantic >=2.3:

    from pydantic import BaseModel, Field\n\n\nclass BinaryPatch(BaseModel):\n\"\"\"\n\n    \"\"\"\n    regions: Ranges\n\n\nmodel = BinaryPatch.model_validate_json(\"\"\"\n{\n    \"regions\": \"0:10, 20:30, 15:, 0x0b\"\n}\n\"\"\")\n\nassert model.regions == \":10,15:\"\n
    "},{"location":"operators/","title":"Set operations","text":"

    They try to be set-like, supporting most of the operations that the set object does.

    Actually, dunno how much of this I'm missing. Raise a ticket if there are gaps.

    Symbol Name Code A \u22c2 B intersection A & B A \u22c3 B union A | B A \u2286 B subset A <= B A \u2282 B proper subset A < B A \u2287 B superset A >= B A \u2283 B proper superset A > B A = B equality A == B A' complement ~A A \\ B relative complement A - B A \u2206 B symmetric difference (A | B) - (A & B) a \u2208 A membership a in A |A| cardinality len(A) \u00d8 empty set Ranges(\"\") \u21150 natural numbers Ranges(\":\")"},{"location":"pydoc/","title":"Pydoc","text":""},{"location":"pydoc/#arranges","title":"arranges","text":""},{"location":"pydoc/#arrangesranges","title":"arranges.ranges","text":""},{"location":"pydoc/#ranges-objects","title":"Ranges Objects","text":"
    class Ranges(str)\n

    A range set that can be hashed and converted to a string.

    "},{"location":"pydoc/#__init__","title":"__init__","text":"
    def __init__(value: Any, stop: range_idx | None = None)\n

    Construct a new string with the canonical form of the range.

    "},{"location":"pydoc/#__new__","title":"__new__","text":"
    def __new__(cls, value: Any, stop: range_idx | None = None) -> str\n

    Construct a new string with the canonical form of the range.

    This becomes \"self\" in init, so we're always a string

    "},{"location":"pydoc/#construct_str","title":"construct_str","text":"
    @classmethod\ndef construct_str(cls, value, stop) -> str\n

    Create a string representation of a range series

    "},{"location":"pydoc/#from_str","title":"from_str","text":"
    @classmethod\ndef from_str(cls, value: str) -> tuple[Segment]\n

    Construct from a string.

    "},{"location":"pydoc/#iterable_to_str","title":"iterable_to_str","text":"
    @classmethod\ndef iterable_to_str(cls, iterable: Iterable) -> str\n

    Convert an iterable of ranges to a string

    "},{"location":"pydoc/#from_hashable_iterable","title":"from_hashable_iterable","text":"
    @classmethod\n@lru_cache\ndef from_hashable_iterable(cls, value: tuple[Any]) -> tuple[Segment]\n

    Cache the result of from_iterable

    "},{"location":"pydoc/#from_iterable","title":"from_iterable","text":"
    @classmethod\ndef from_iterable(cls, iterable: Iterable) -> tuple[Segment]\n

    Sort and merge a list of ranges.

    "},{"location":"pydoc/#__eq__","title":"__eq__","text":"
    def __eq__(other: Any) -> bool\n

    Compare the two lists based on their string representations

    "},{"location":"pydoc/#__contains__","title":"__contains__","text":"
    def __contains__(other: Any) -> bool\n

    Are all of the other ranges in our ranges?

    "},{"location":"pydoc/#__iter__","title":"__iter__","text":"
    def __iter__()\n

    Iterate over the values in our ranges.

    Note that this could be boundless.

    "},{"location":"pydoc/#intersects","title":"intersects","text":"
    def intersects(other: Any) -> bool\n

    True if this range overlaps with the other range

    "},{"location":"pydoc/#union","title":"union","text":"
    def union(other) -> \"Ranges\"\n

    Return the union of this range and the other

    "},{"location":"pydoc/#__or__","title":"__or__","text":"
    def __or__(other: \"Ranges\") -> \"Ranges\"\n

    Return the union of this range and the other

    "},{"location":"pydoc/#__and__","title":"__and__","text":"
    def __and__(other: \"Ranges\") -> \"Ranges\"\n

    Return the intersection of this range and the other

    "},{"location":"pydoc/#__invert__","title":"__invert__","text":"
    def __invert__()\n

    The inverse of this range

    "},{"location":"pydoc/#validate","title":"validate","text":"
    @classmethod\ndef validate(cls, value: Any) -> \"Ranges\"\n

    Validate a value and convert it to a Range

    "},{"location":"pydoc/#__get_pydantic_core_schema__","title":"__get_pydantic_core_schema__","text":"
    @classmethod\ndef __get_pydantic_core_schema__(cls, source_type: Any,\n                                 handler: GetCoreSchemaHandler) -> CoreSchema\n

    For automatic validation in pydantic

    "},{"location":"pydoc/#arrangesutils","title":"arranges.utils","text":"

    Some quacky type helpers so we don't have to use isinstance everywhere

    "},{"location":"pydoc/#_boundless-objects","title":"_Boundless Objects","text":"
    class _Boundless(float)\n

    A class that represents a boundless end of a range

    "},{"location":"pydoc/#huge","title":"huge","text":"

    An enormous number that's used as a stand-in for infinity

    "},{"location":"pydoc/#__eq___1","title":"__eq__","text":"
    def __eq__(other) -> bool\n

    Is this boundless?

    "},{"location":"pydoc/#__index__","title":"__index__","text":"
    def __index__() -> int\n

    When used as an index, return a huge integer rather than infinity.

    This is necessary because CPython doesn't allow lengths larger than sys.maxsize, and Python has no way to represent infinity as an integer.

    "},{"location":"pydoc/#inf","title":"inf","text":"

    A boundless end of a range. When used as a stop value it's infinite, but when used as a length it's the largest index integer possible in cpython.

    "},{"location":"pydoc/#to_int","title":"to_int","text":"
    def to_int(value: str, default: int) -> int\n

    Convert a string to an integer. If the string is empty, return the default

    "},{"location":"pydoc/#is_rangelike","title":"is_rangelike","text":"
    def is_rangelike(obj: Any) -> bool\n

    Check if a value is a range-like object

    "},{"location":"pydoc/#is_intlike","title":"is_intlike","text":"
    def is_intlike(value: Any) -> bool\n

    Can this object be converted to an integer?

    "},{"location":"pydoc/#is_iterable","title":"is_iterable","text":"
    def is_iterable(value: Any) -> bool\n

    Is this object iterable?

    "},{"location":"pydoc/#as_type","title":"as_type","text":"
    def as_type(cls: Type[T], value: Any) -> T\n

    Convert a value to a type, if necessary.

    Saves a bit of construction time if the value is already the right type.

    "},{"location":"pydoc/#try_hash","title":"try_hash","text":"
    def try_hash(obj: Any) -> int | None\n

    Try to hash an object. If it can't be hashed, return None

    "},{"location":"pydoc/#arrangessegment","title":"arranges.segment","text":""},{"location":"pydoc/#start_stop_to_str","title":"start_stop_to_str","text":"
    def start_stop_to_str(start: range_idx, stop: range_idx) -> str\n

    Returns a string representation of a range from start to stop.

    "},{"location":"pydoc/#segment-objects","title":"Segment Objects","text":"
    class Segment(str)\n

    A single range segment that's a string and can be hashed.

    "},{"location":"pydoc/#__init___1","title":"__init__","text":"
    def __init__(start: range_idx, stop: range_idx = None)\n

    Construct a new string with the canonical form of the range.

    "},{"location":"pydoc/#__new___1","title":"__new__","text":"
    def __new__(cls, start: range_idx, stop: range_idx = None) -> str\n

    Construct a new string with the canonical form of the range.

    "},{"location":"pydoc/#__or___1","title":"__or__","text":"
    def __or__(other: \"Segment\") -> \"Segment\"\n

    Return the union of this range and the other range

    "},{"location":"pydoc/#__iter___1","title":"__iter__","text":"
    def __iter__()\n

    Iterate over the values in this range

    "},{"location":"pydoc/#__len__","title":"__len__","text":"
    def __len__() -> int\n

    Get the length of this range

    "},{"location":"pydoc/#__bool__","title":"__bool__","text":"
    def __bool__() -> bool\n

    True if this range has a length

    "},{"location":"pydoc/#last","title":"last","text":"
    @property\ndef last() -> int\n

    Gets the last value in this range. Will return inf if the range has no end, and -1 if it has no contents,

    "},{"location":"pydoc/#from_str_1","title":"from_str","text":"
    @classmethod\n@lru_cache\ndef from_str(cls, value: str) -> \"Segment\"\n

    Construct from a string.

    "},{"location":"pydoc/#sort_key","title":"sort_key","text":"
    @staticmethod\ndef sort_key(value: \"Segment\") -> tuple[int, int]\n

    Sort key function for sorting ranges

    "},{"location":"pydoc/#isdisjoint","title":"isdisjoint","text":"
    def isdisjoint(other: Any) -> bool\n

    Return True if this range is disjoint from the other range

    "},{"location":"pydoc/#__eq___2","title":"__eq__","text":"
    def __eq__(other: Any) -> bool\n

    Compare two segments

    "},{"location":"pydoc/#isconnected","title":"isconnected","text":"
    def isconnected(other: \"Segment\") -> bool\n

    True if this range is adjacent to or overlaps the other range, and so they can be joined together.

    "},{"location":"pydoc/#isadjacent","title":"isadjacent","text":"
    def isadjacent(other: \"Segment\") -> bool\n

    True if this range is adjacent to the other range

    "},{"location":"pydoc/#intersects_1","title":"intersects","text":"
    def intersects(other: \"Segment\") -> bool\n

    True if this range intersects the other range.

    "},{"location":"pydoc/#__contains___1","title":"__contains__","text":"
    def __contains__(other: Any) -> bool\n

    Membership test. Supports integers, strings, ranges and iterables.

    "}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Arranges","text":""},{"location":"#range-string-fields-for-pydantic-basemodels","title":"Range string fields for Pydantic BaseModels","text":"

    I needed a way to parse batches of byte, row and line and other object ranges in my merge-files app, in a way that I can just drop it in as a string field type. The reason for this is so the machine-generated command line help is flat and readable by humans.

    It it kinda grew into a monster so I've split it out into this separate package. The main feature is a pair of classes that can represent ranges:

    The range class is designed to be used as fields in Pydantic BaseModels, but can be used anywhere you need a range. They are not designed with speed in mind, and comparisons usually use the canonical string form by converting other things into Ranges objects. Their preferred pronoun is they/them.

    "},{"location":"#constraints","title":"Constraints","text":"

    I made them to select lines or bytes in a stream of data, so they:

    "},{"location":"#installation","title":"Installation","text":"

    pip install arranges if you want to use them. You'll need Python 3.10 or above.

    "},{"location":"#dev-setup","title":"Dev setup","text":"

    To add features etc you'll ideally need git, make, bash and something with a debugger. Config for Visual Studio Code is included.

    Clone the repo and make dev to make the venv, install dependencies, then code . to open the project up in the venv with tests and debugging and all that jazz.

    Type make help to see the other options, or run the one-liner scripts in the ./build dir if you want to run steps without all that fancy caching nonsense.

    "},{"location":"#usage","title":"Usage","text":""},{"location":"#license","title":"License","text":"

    Free as in freedom from legalese; the WTFPL with a warranty clause.

    Political note: I don't want to live in a world where lawyers tell me how to speak. If you don't trust me enough to use the WTFPL then you shouldn't be running my code in the first place.

    "},{"location":"LICENSE/","title":"License","text":"

    Licensed under the WTFPL with one additional clause:

    1. Don't blame me.

    Do whatever the fuck you want, just don't blame me.

    "},{"location":"construction/","title":"Constructing Range and Ranges","text":"

    You can create them from:

    1. strings
    2. range-like objects
    3. sequences of ints or these things, or other sequences
    4. explicitly, like you would create a range or slice
    "},{"location":"construction/#range-from-a-string","title":"Range from a string","text":"
    from arranges import Ranges\n# Create ranges from a string. Like a slice but without the square brackets.\nthe_full_range = Ranges(\":\")  # an endless sequence starting at 0\nthe_empty_range = Ranges(\"\")  # start, stop and len() are all 0\nfrom_0_to_99 = Ranges(\"0:100\")\nskip_10_items = Ranges(\"10:\")  # boundless, goes to infinity\naccess_by_index = Ranges(\"0\")  # just the first item\n# Decimal, hexadecimal, octal and binary are supported. Note that octal uses\n# Python's 0o prefix, not 0 like in C.\ngbc_palette = Ranges(\"0xff68:0xff88\")\nskip_bmp_header = Ranges(\"0o66:end\")  # skip \"54\", \"0x36\" or \"0b1100110\" bytes\n# \"end\" and \"inf\" are the same. You can also use \"start\" instead of 0.\nthe_full_range_again = Ranges(\"start:inf\")\n# whitespace is ignored during construction\nfirst_kilobyte = Ranges(\"start : 0x400\")\n# They are simplified when converted to str, which they can be compared with.\nassert first_kilobyte == \":1024\"\n# Comparisons work in both directions\nassert \"000001:000002\" == Ranges(\"1\")\n# and the hash is the same as the string\nw = Ranges(\"6 : 8\")\nassert hash(w) == hash(\"6:8\")\n# so you can use them as dict keys\nd = {\"6:8\": \"width\"}\nd[w] = \"GIF file width\"\nassert d == {\"6:8\": \"GIF file width\"}\n
    "},{"location":"construction/#ranges-from-a-string","title":"Ranges from a string","text":"
    from arranges import Ranges\n# You can put gaps in your range using commas.\npages_2_to_5_and_20 = Ranges(\"2:6, 20\")\n# The ranges are sorted and combined as they are added\noverlapping = Ranges(\"start:10, 14, 1:3\")\nassert overlapping == \":10,14\"\n# But you can't hash Ranges because they're mutable\nimport pytest\nwith pytest.raises(ValueError):\na = {overlapping: \"whatever\"}\n
    "},{"location":"construction/#ranges-from-range-like-objects","title":"Ranges from range-like objects","text":"

    If it has a start, stop and step attribute then it quacks like a range so you can pass it in as the value. If the step value is something other than None or 1 then it'll raise ValueError.

    from arranges import Ranges\nfrom_slice = Ranges(slice(10, 20))\nfrom_range = Ranges(range(10, 20))\nfrom_other = Ranges(Ranges(\"10:20\"))\nassert from_slice == from_range == from_other == \"10:20\"\n
    "},{"location":"construction/#ranges-from-sequences","title":"Ranges from sequences","text":"
    from arranges import Ranges\nl = list(range(100))\nt = tuple(range(100))\ni = (i for i in range(100))\nassert Ranges(l) == Ranges(t) == Ranges(i) == \":100\"\njumble = Ranges([0, [2, 4], [\"1:3\"], 3, 4, range(10, 20)])\nassert jumble == \"5,10:20\"\n
    "},{"location":"construction/#explicitly-creating-ranges","title":"Explicitly creating Ranges","text":"

    You can create them in the same way as range or slice objects which have a slightly different syntax to slice notation.

    from arranges import Ranges, inf\nassert Ranges(10, inf) == \"10:\"\n# Ranges(\"10\") is not the same as Ranges(10)\nassert Ranges(10) == \":10\"\nassert Ranges(\"10\") == 10 == Ranges(10, 11)\n
    "},{"location":"iteration/","title":"Iteration","text":"

    Ranges objects are sequences of integers that you can iterate over.

    from arranges import Ranges\nassert list(Ranges(\"start:3, 10\")) == [0, 1, 2, 10]\n
    "},{"location":"iteration/#cardinality-of-the-address-range","title":"Cardinality of the address range","text":"

    The length of a range is the number of elements it contains. But because ranges can be boundless, we have a special inf value that represents an infinite int as Python doesn't have or support one.

    This value is math.inf, but when returned from len it becomes sys.maxsize as it only supports ints that are this size or smaller. When comparing inf to sys.maxsize it'll return True, but this doesn't work in all directions:

    import math\nimport sys\nfrom arranges import Ranges, inf\nassert len(Ranges(\"1,2,3,4,10:20\")) == 14\nfull = Ranges(\":\")\nassert len(full) == sys.maxsize\nassert len(full) == inf\nassert sys.maxsize == inf\nassert full.stop == inf\nassert inf - 100 == sys.maxsize\n# Careful though, this does not hold\nassert len(full) != math.inf\n# because it's an int\nassert type(len(full)) is int\n
    "},{"location":"iteration/#truthyness","title":"Truthyness","text":"

    Empty ranges are, of course, Falsey.

    from arranges import Ranges\nassert Ranges(\":\")\nassert Range(10)\nassert not Ranges(\"\")\n
    "},{"location":"models/","title":"Use in Pydantic models","text":"

    You'll need Pydantic >=2.3:

    from pydantic import BaseModel, Field\nclass BinaryPatch(BaseModel):\n\"\"\"\n    \"\"\"\nregions: Ranges\nmodel = BinaryPatch.model_validate_json(\"\"\"\n{\n    \"regions\": \"0:10, 20:30, 15:, 0x0b\"\n}\n\"\"\")\nassert model.regions == \":10,15:\"\n
    "},{"location":"operators/","title":"Set operations","text":"

    They try to be set-like, supporting most of the operations that the set object does.

    Actually, dunno how much of this I'm missing. Raise a ticket if there are gaps.

    Symbol Name Code A \u22c2 B intersection A & B A \u22c3 B union A | B A \u2286 B subset A <= B A \u2282 B proper subset A < B A \u2287 B superset A >= B A \u2283 B proper superset A > B A = B equality A == B A' complement ~A A \\ B relative complement A - B A \u2206 B symmetric difference (A | B) - (A & B) a \u2208 A membership a in A |A| cardinality len(A) \u00d8 empty set Ranges(\"\") \u21150 natural numbers Ranges(\":\")"},{"location":"pydoc/","title":"Pydoc","text":""},{"location":"pydoc/#arranges","title":"arranges","text":""},{"location":"pydoc/#arrangesranges","title":"arranges.ranges","text":""},{"location":"pydoc/#ranges-objects","title":"Ranges Objects","text":"
    class Ranges(str)\n

    A range set that can be hashed and converted to a string.

    "},{"location":"pydoc/#__init__","title":"__init__","text":"
    def __init__(value: Any, stop: range_idx | None = None)\n

    Construct a new string with the canonical form of the range.

    "},{"location":"pydoc/#__new__","title":"__new__","text":"
    def __new__(cls, value: Any, stop: range_idx | None = None) -> str\n

    Construct a new string with the canonical form of the range.

    This becomes \"self\" in init, so we're always a string

    "},{"location":"pydoc/#construct_str","title":"construct_str","text":"
    @classmethod\ndef construct_str(cls, value, stop) -> str\n

    Create a string representation of a range series

    "},{"location":"pydoc/#from_str","title":"from_str","text":"
    @classmethod\ndef from_str(cls, value: str) -> tuple[Segment]\n

    Construct from a string.

    "},{"location":"pydoc/#iterable_to_str","title":"iterable_to_str","text":"
    @classmethod\ndef iterable_to_str(cls, iterable: Iterable) -> str\n

    Convert an iterable of ranges to a string

    "},{"location":"pydoc/#from_hashable_iterable","title":"from_hashable_iterable","text":"
    @classmethod\n@lru_cache\ndef from_hashable_iterable(cls, value: tuple[Any]) -> tuple[Segment]\n

    Cache the result of from_iterable

    "},{"location":"pydoc/#from_iterable","title":"from_iterable","text":"
    @classmethod\ndef from_iterable(cls, iterable: Iterable) -> tuple[Segment]\n

    Sort and merge a list of ranges.

    "},{"location":"pydoc/#__eq__","title":"__eq__","text":"
    def __eq__(other: Any) -> bool\n

    Compare the two lists based on their string representations

    "},{"location":"pydoc/#__contains__","title":"__contains__","text":"
    def __contains__(other: Any) -> bool\n

    Are all of the other ranges in our ranges?

    "},{"location":"pydoc/#__iter__","title":"__iter__","text":"
    def __iter__()\n

    Iterate over the values in our ranges.

    Note that this could be boundless.

    "},{"location":"pydoc/#intersects","title":"intersects","text":"
    def intersects(other: Any) -> bool\n

    True if this range overlaps with the other range

    "},{"location":"pydoc/#union","title":"union","text":"
    def union(other) -> \"Ranges\"\n

    Return the union of this range and the other

    "},{"location":"pydoc/#__or__","title":"__or__","text":"
    def __or__(other: \"Ranges\") -> \"Ranges\"\n

    Return the union of this range and the other

    "},{"location":"pydoc/#__and__","title":"__and__","text":"
    def __and__(other: \"Ranges\") -> \"Ranges\"\n

    Return the intersection of this range and the other

    "},{"location":"pydoc/#__invert__","title":"__invert__","text":"
    def __invert__()\n

    The inverse of this range

    "},{"location":"pydoc/#validate","title":"validate","text":"
    @classmethod\ndef validate(cls, value: Any) -> \"Ranges\"\n

    Validate a value and convert it to a Range

    "},{"location":"pydoc/#__get_pydantic_core_schema__","title":"__get_pydantic_core_schema__","text":"
    @classmethod\ndef __get_pydantic_core_schema__(cls, source_type: Any,\nhandler: GetCoreSchemaHandler) -> CoreSchema\n

    For automatic validation in pydantic

    "},{"location":"pydoc/#arrangesutils","title":"arranges.utils","text":"

    Some quacky type helpers so we don't have to use isinstance everywhere

    "},{"location":"pydoc/#_boundless-objects","title":"_Boundless Objects","text":"
    class _Boundless(float)\n

    A class that represents a boundless end of a range

    "},{"location":"pydoc/#huge","title":"huge","text":"

    An enormous number that's used as a stand-in for infinity

    "},{"location":"pydoc/#__eq___1","title":"__eq__","text":"
    def __eq__(other) -> bool\n

    Is this boundless?

    "},{"location":"pydoc/#__index__","title":"__index__","text":"
    def __index__() -> int\n

    When used as an index, return a huge integer rather than infinity.

    This is necessary because CPython doesn't allow lengths larger than sys.maxsize, and Python has no way to represent infinity as an integer.

    "},{"location":"pydoc/#inf","title":"inf","text":"

    A boundless end of a range. When used as a stop value it's infinite, but when used as a length it's the largest index integer possible in cpython.

    "},{"location":"pydoc/#to_int","title":"to_int","text":"
    def to_int(value: str, default: int) -> int\n

    Convert a string to an integer. If the string is empty, return the default

    "},{"location":"pydoc/#is_rangelike","title":"is_rangelike","text":"
    def is_rangelike(obj: Any) -> bool\n

    Check if a value is a range-like object

    "},{"location":"pydoc/#is_intlike","title":"is_intlike","text":"
    def is_intlike(value: Any) -> bool\n

    Can this object be converted to an integer?

    "},{"location":"pydoc/#is_iterable","title":"is_iterable","text":"
    def is_iterable(value: Any) -> bool\n

    Is this object iterable?

    "},{"location":"pydoc/#as_type","title":"as_type","text":"
    def as_type(cls: Type[T], value: Any) -> T\n

    Convert a value to a type, if necessary.

    Saves a bit of construction time if the value is already the right type.

    "},{"location":"pydoc/#try_hash","title":"try_hash","text":"
    def try_hash(obj: Any) -> int | None\n

    Try to hash an object. If it can't be hashed, return None

    "},{"location":"pydoc/#arrangessegment","title":"arranges.segment","text":""},{"location":"pydoc/#start_stop_to_str","title":"start_stop_to_str","text":"
    def start_stop_to_str(start: range_idx, stop: range_idx) -> str\n

    Returns a string representation of a range from start to stop.

    "},{"location":"pydoc/#segment-objects","title":"Segment Objects","text":"
    class Segment(str)\n

    A single range segment that's a string and can be hashed.

    "},{"location":"pydoc/#__init___1","title":"__init__","text":"
    def __init__(start: range_idx, stop: range_idx = None)\n

    Construct a new string with the canonical form of the range.

    "},{"location":"pydoc/#__new___1","title":"__new__","text":"
    def __new__(cls, start: range_idx, stop: range_idx = None) -> str\n

    Construct a new string with the canonical form of the range.

    "},{"location":"pydoc/#__or___1","title":"__or__","text":"
    def __or__(other: \"Segment\") -> \"Segment\"\n

    Return the union of this range and the other range

    "},{"location":"pydoc/#__iter___1","title":"__iter__","text":"
    def __iter__()\n

    Iterate over the values in this range

    "},{"location":"pydoc/#__len__","title":"__len__","text":"
    def __len__() -> int\n

    Get the length of this range

    "},{"location":"pydoc/#__bool__","title":"__bool__","text":"
    def __bool__() -> bool\n

    True if this range has a length

    "},{"location":"pydoc/#last","title":"last","text":"
    @property\ndef last() -> int\n

    Gets the last value in this range. Will return inf if the range has no end, and -1 if it has no contents,

    "},{"location":"pydoc/#from_str_1","title":"from_str","text":"
    @classmethod\n@lru_cache\ndef from_str(cls, value: str) -> \"Segment\"\n

    Construct from a string.

    "},{"location":"pydoc/#sort_key","title":"sort_key","text":"
    @staticmethod\ndef sort_key(value: \"Segment\") -> tuple[int, int]\n

    Sort key function for sorting ranges

    "},{"location":"pydoc/#isdisjoint","title":"isdisjoint","text":"
    def isdisjoint(other: Any) -> bool\n

    Return True if this range is disjoint from the other range

    "},{"location":"pydoc/#__eq___2","title":"__eq__","text":"
    def __eq__(other: Any) -> bool\n

    Compare two segments

    "},{"location":"pydoc/#isconnected","title":"isconnected","text":"
    def isconnected(other: \"Segment\") -> bool\n

    True if this range is adjacent to or overlaps the other range, and so they can be joined together.

    "},{"location":"pydoc/#isadjacent","title":"isadjacent","text":"
    def isadjacent(other: \"Segment\") -> bool\n

    True if this range is adjacent to the other range

    "},{"location":"pydoc/#intersects_1","title":"intersects","text":"
    def intersects(other: \"Segment\") -> bool\n

    True if this range intersects the other range.

    "},{"location":"pydoc/#__contains___1","title":"__contains__","text":"
    def __contains__(other: Any) -> bool\n

    Membership test. Supports integers, strings, ranges and iterables.

    "}]} \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 416c5aa..dd28624 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ