logo Netzschleuder network catalog, repository, and centrifuge

History of debian_dependencies

The changes to the source code of this entry in the repository, newest first, each with the differences it made to the entry's files. They are also listed on GitLab.

[entries] Add the date when each entry was added, and remove icon_hash by Tiago de Paula Peixoto beff7c9
The dates are those of the commits that first added the entries' files
(2020-08-11 for the entries of the initial commit); renamed entries keep
the date of their first name.

Changed: none
diff --git a/entries/debian_dependencies/__init__.py b/entries/debian_dependencies/__init__.py
index 3c1a34a..63831d4 100644
--- a/entries/debian_dependencies/__init__.py
+++ b/entries/debian_dependencies/__init__.py
@@ -99,7 +99,7 @@ url = ['https://www.debian.org/releases/', 'https://archive.debian.org/debian/di
        'https://snapshot.debian.org/']
 citation = [('M. A. Fortuna, J. A. Bonachela and S. A. Levin, "Evolution of a modular software network." Proc. Natl. Acad. Sci. USA 108(50), 19985-19989 (2011)',
              'https://doi.org/10.1073/pnas.1115960108')]
-icon_hash = None
+date_added = '2026-10-31'
 upstream_license = None
 
 upstream_prefix = None
[entries] Prefix the properties in the descriptions with (Node), (Edge) or (Graph) by Tiago de Paula Peixoto bc3dbbd
The lists of properties at the end of the descriptions marked edge
properties with "Edge" but left node properties unmarked, or grouped
them in "Nodes: `a`, ...; `b`, ..." items, so the kind of a property
was often unclear. Every item now starts with (Node), (Edge) or (Graph),
followed by the property names, a colon and the explanation, one item
per property or group of properties; qualifications (kinds of nodes,
networks) follow the names in parentheses. Properties that were only
named got a short explanation, and a few items that named the wrong
kind of property were corrected.
diff --git a/entries/debian_dependencies/__init__.py b/entries/debian_dependencies/__init__.py
index a7ff27f..3c1a34a 100644
--- a/entries/debian_dependencies/__init__.py
+++ b/entries/debian_dependencies/__init__.py
@@ -79,21 +79,21 @@ The archived releases are those of the final point release (e.g. 11.11 for bulls
 
 The networks have the following properties:
 
-- `name`: the name of the package.
-- `kind`: `package` for the packages of the index, `virtual` for names provided by some package but without a package of their own, and `missing` for names that only appear in relationships.
-- `version`: the version of the package (with the revision, for 0.93R6).
-- `component`: the component of the archive (`main`, `contrib`, `non-free`, `non-free-firmware`; empty for 0.93R6, whose index has no components).
-- `section`, `priority`: the section (e.g. `admin`, `libs`, `net`) and priority (`required`, `important`, `standard`, `optional`, `extra`) of the package.
-- `essential`: whether the package is marked essential.
-- `architecture`: `i386` or `all` (architecture-independent packages; empty when not given).
-- `source`: the source package, if it differs from the package's name (and it is given).
-- `maintainer`: the maintainer of the package, as given in the index.
-- `installed_size`: the installed size in kilobytes (-1 if not given), and `size`, the size of the package file in bytes (-1 if not given).
-- `description`: the short (one-line) description of the package.
-- Edge `type`: the field of the relationship (`Depends`, `Pre-Depends`, `Recommends`, `Suggests`, `Enhances`, `Conflicts`, `Breaks`, `Replaces`, `Provides`).
-- Edge `constraint`: the version constraint of the relationship (e.g. `>= 2.3`), or empty.
-- Edge `alternatives`: the number of alternatives of the dependency (1 if there are none).
-- Graph properties: `release` (the version number), `codename` and `release_date` (of the first release of the version, YYYY-MM-DD; only the month for 0.93R6)."""
+- (Node) `name`: the name of the package.
+- (Node) `kind`: `package` for the packages of the index, `virtual` for names provided by some package but without a package of their own, and `missing` for names that only appear in relationships.
+- (Node) `version`: the version of the package (with the revision, for 0.93R6).
+- (Node) `component`: the component of the archive (`main`, `contrib`, `non-free`, `non-free-firmware`; empty for 0.93R6, whose index has no components).
+- (Node) `section`, `priority`: the section (e.g. `admin`, `libs`, `net`) and priority (`required`, `important`, `standard`, `optional`, `extra`) of the package.
+- (Node) `essential`: whether the package is marked essential.
+- (Node) `architecture`: `i386` or `all` (architecture-independent packages; empty when not given).
+- (Node) `source`: the source package, if it differs from the package's name (and it is given).
+- (Node) `maintainer`: the maintainer of the package, as given in the index.
+- (Node) `installed_size`: the installed size in kilobytes (-1 if not given), and `size`, the size of the package file in bytes (-1 if not given).
+- (Node) `description`: the short (one-line) description of the package.
+- (Edge) `type`: the field of the relationship (`Depends`, `Pre-Depends`, `Recommends`, `Suggests`, `Enhances`, `Conflicts`, `Breaks`, `Replaces`, `Provides`).
+- (Edge) `constraint`: the version constraint of the relationship (e.g. `>= 2.3`), or empty.
+- (Edge) `alternatives`: the number of alternatives of the dependency (1 if there are none).
+- (Graph) `release` (the version number), `codename` and `release_date` (of the first release of the version, YYYY-MM-DD; only the month for 0.93R6)."""
 tags = ['Technological', 'Software', 'Unweighted', 'Multigraph', 'Metadata']
 url = ['https://www.debian.org/releases/', 'https://archive.debian.org/debian/dists/',
        'https://snapshot.debian.org/']
[entries] New entry: debian_dependencies by Tiago de Paula Peixoto 50bb722
diff --git a/entries/debian_dependencies/__init__.py b/entries/debian_dependencies/__init__.py
new file mode 100644
index 0000000..a7ff27f
--- /dev/null
+++ b/entries/debian_dependencies/__init__.py
@@ -0,0 +1,258 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2026 Tiago de Paula Peixoto <tiago@skewed.de>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Affero General Public License along
+# with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import re
+
+from .. import *
+
+# The releases: (network name, version, release date, components); the
+# archived releases are taken from archive.debian.org, the supported ones
+# (bookworm, trixie) from a pinned snapshot of snapshot.debian.org
+RELEASES = [("0.93r6", "0.93R6", "1995-11", None),
+            ("buzz", "1.1", "1996-06-17", ["main", "contrib"]),
+            ("rex", "1.2", "1996-12-12", ["main", "contrib"]),
+            ("bo", "1.3", "1997-06-05", ["main", "contrib", "non-free"]),
+            ("hamm", "2.0", "1998-07-24", ["main", "contrib", "non-free"]),
+            ("slink", "2.1", "1999-03-09", ["main", "contrib", "non-free"]),
+            ("potato", "2.2", "2000-08-15", ["main", "contrib", "non-free"]),
+            ("woody", "3.0", "2002-07-19", ["main", "contrib", "non-free"]),
+            ("sarge", "3.1", "2005-06-06", ["main", "contrib", "non-free"]),
+            ("etch", "4.0", "2007-04-08", ["main", "contrib", "non-free"]),
+            ("lenny", "5.0", "2009-02-14", ["main", "contrib", "non-free"]),
+            ("squeeze", "6.0", "2011-02-06", ["main", "contrib", "non-free"]),
+            ("wheezy", "7", "2013-05-04", ["main", "contrib", "non-free"]),
+            ("jessie", "8", "2015-04-25", ["main", "contrib", "non-free"]),
+            ("stretch", "9", "2017-06-17", ["main", "contrib", "non-free"]),
+            ("buster", "10", "2019-07-06", ["main", "contrib", "non-free"]),
+            ("bullseye", "11", "2021-08-14", ["main", "contrib", "non-free"]),
+            ("bookworm", "12", "2023-06-10",
+             ["main", "contrib", "non-free", "non-free-firmware"]),
+            ("trixie", "13", "2025-08-09",
+             ["main", "contrib", "non-free", "non-free-firmware"])]
+
+ARCHIVE = "https://archive.debian.org/debian/dists"
+SNAPSHOT = "https://snapshot.debian.org/archive/debian/20261030T000000Z/dists"
+
+def _urls():
+    "The upstream files, per release, as {fname: url}."
+    out = {}
+    for rel, ver, date, comps in RELEASES:
+        fs = {}
+        if comps is None:
+            fs["Debian-0.93R6/Packages"] = f"{ARCHIVE}/Debian-0.93R6/Packages"
+        else:
+            for c in comps:
+                if rel in ("bookworm", "trixie"):
+                    p = f"{rel}/{c}/binary-i386/Packages.xz"
+                    fs[p] = f"{SNAPSHOT}/{p}"
+                else:
+                    # (buzz has its contrib index in "binary")
+                    sub = "binary" if (rel, c) == ("buzz", "contrib") else "binary-i386"
+                    p = f"{rel}/{c}/{sub}/Packages.gz"
+                    fs[p] = f"{ARCHIVE}/{p}"
+        out[rel] = fs
+    return out
+
+URLS = _urls()
+
+title = "Debian package dependencies (1995-2025)"
+description = r"""Networks of the dependencies between the binary packages of the Debian GNU/Linux distribution, one for each stable release, from Debian 0.93R6 (1995) and 1.1 "buzz" (1996) to 13 "trixie" (2025), built from the package indices (the `Packages` files) of the Debian archive, for the i386 architecture (the only one present in all releases), with all the components of each release (`main`, and `contrib`, `non-free` and `non-free-firmware`, when present).
+
+Nodes are packages, and a directed edge from package $i$ to package $j$ is a relationship declared by $i$ in its control fields: a dependency (`Depends`, `Pre-Depends`), a weaker dependency (`Recommends`, `Suggests`, `Enhances`), a conflict (`Conflicts`, `Breaks`), a replacement (`Replaces`), or the provision of a virtual package (`Provides`). Dependencies of the form "a | b" (any of several alternatives) give an edge to each alternative, with the number of alternatives as an edge property. The packages named in relationships that are not in the index of the release (other architectures, removed packages, ...) are also nodes, as are the virtual packages (names provided by other packages, e.g. `mail-transport-agent`), with a property that tells them apart; architecture qualifiers (e.g. `:any`) and restrictions to architectures or build profiles are left out. Packages listed several times in an index, with several versions (a few in most releases, 223 in jessie), are taken once, with their first entry.
+
+The archived releases are those of the final point release (e.g. 11.11 for bullseye), as kept on archive.debian.org; the releases bookworm (12) and trixie (13), still updated in place, are taken from the snapshot of 30 October 2026 of snapshot.debian.org. The networks are named after the codename of the release (`0.93r6` for Debian 0.93R6, which has no codename).
+
+The networks have the following properties:
+
+- `name`: the name of the package.
+- `kind`: `package` for the packages of the index, `virtual` for names provided by some package but without a package of their own, and `missing` for names that only appear in relationships.
+- `version`: the version of the package (with the revision, for 0.93R6).
+- `component`: the component of the archive (`main`, `contrib`, `non-free`, `non-free-firmware`; empty for 0.93R6, whose index has no components).
+- `section`, `priority`: the section (e.g. `admin`, `libs`, `net`) and priority (`required`, `important`, `standard`, `optional`, `extra`) of the package.
+- `essential`: whether the package is marked essential.
+- `architecture`: `i386` or `all` (architecture-independent packages; empty when not given).
+- `source`: the source package, if it differs from the package's name (and it is given).
+- `maintainer`: the maintainer of the package, as given in the index.
+- `installed_size`: the installed size in kilobytes (-1 if not given), and `size`, the size of the package file in bytes (-1 if not given).
+- `description`: the short (one-line) description of the package.
+- Edge `type`: the field of the relationship (`Depends`, `Pre-Depends`, `Recommends`, `Suggests`, `Enhances`, `Conflicts`, `Breaks`, `Replaces`, `Provides`).
+- Edge `constraint`: the version constraint of the relationship (e.g. `>= 2.3`), or empty.
+- Edge `alternatives`: the number of alternatives of the dependency (1 if there are none).
+- Graph properties: `release` (the version number), `codename` and `release_date` (of the first release of the version, YYYY-MM-DD; only the month for 0.93R6)."""
+tags = ['Technological', 'Software', 'Unweighted', 'Multigraph', 'Metadata']
+url = ['https://www.debian.org/releases/', 'https://archive.debian.org/debian/dists/',
+       'https://snapshot.debian.org/']
+citation = [('M. A. Fortuna, J. A. Bonachela and S. A. Levin, "Evolution of a modular software network." Proc. Natl. Acad. Sci. USA 108(50), 19985-19989 (2011)',
+             'https://doi.org/10.1073/pnas.1115960108')]
+icon_hash = None
+upstream_license = None
+
+upstream_prefix = None
+files = [(tuple(URLS[rel]), rel, None) for rel, ver, date, comps in RELEASES]
+
+def fetch_upstream(force=False):
+    name = __name__.split(".")[-1]
+    return fetch_archived_files(name, {f: u for fs in URLS.values()
+                                       for f, u in fs.items()},
+                                force=force, tries=3)
+
+# The relationship fields, in the order of the edges
+REL_FIELDS = ["pre-depends", "depends", "recommends", "suggests", "enhances",
+              "conflicts", "breaks", "replaces", "provides"]
+REL_NAMES = {"pre-depends": "Pre-Depends", "depends": "Depends",
+             "recommends": "Recommends", "suggests": "Suggests",
+             "enhances": "Enhances", "conflicts": "Conflicts",
+             "breaks": "Breaks", "replaces": "Replaces",
+             "provides": "Provides"}
+
+_REL = re.compile(r"\s*([^\s(\[<:]+)(?::\S+)?\s*(?:\(\s*([^)]*?)\s*\))?")
+
+def _stanzas(f):
+    "The stanzas of a Packages file, as dicts with lower-case keys."
+    st = {}
+    key = None
+    for line in f:
+        line = line.rstrip("\n")
+        if not line.strip():
+            if st:
+                yield st
+            st = {}
+            key = None
+            continue
+        if line[0] in " \t":
+            if key is not None:
+                st[key] += "\n" + line
+            continue
+        k, sep, v = line.partition(":")
+        if not sep:
+            continue
+        key = k.strip().lower()
+        st[key] = v.strip()
+    if st:
+        yield st
+
+def _relations(value):
+    "The relationships of a field, as [(clause, [(name, constraint), ...])]."
+    out = []
+    for clause in value.replace("\n", " ").split(","):
+        alts = []
+        for a in clause.split("|"):
+            m = _REL.match(a)
+            if m is None or not m.group(1):
+                continue
+            c = re.sub(r"\s+", " ", m.group(2) or "")
+            # (old indices write e.g. "(>=2.3)")
+            c = re.sub(r"^([<>=]+)\s*", r"\1 ", c)
+            alts.append((m.group(1).strip(), c))
+        if alts:
+            out.append(alts)
+    return out
+
+@cache_network()
+@coerce_props()
+@annotate()
+def parse(alts=None):
+    name = __name__.split(".")[-1]
+    for fnames, alt, fmt in files:
+        if alts is not None and alt not in alts:
+            continue
+        rel = [r for r in RELEASES if r[0] == alt][0]
+        pkgs = {}
+        for fn in fnames:
+            comp = fn.split("/")[1] if rel[3] is not None else ""
+            with open_upstream_file(name, fn, "r", errors="replace") as f:
+                for st in _stanzas(f):
+                    p = st.get("package")
+                    if p is None or p in pkgs:
+                        continue
+                    st["_component"] = comp
+                    pkgs[p] = st
+
+        g = Graph(directed=True)
+        vprops = [("name", "string"), ("kind", "string"), ("version", "string"),
+                  ("component", "string"), ("section", "string"),
+                  ("priority", "string"), ("essential", "bool"),
+                  ("architecture", "string"), ("source", "string"),
+                  ("maintainer", "string"), ("installed_size", "int64_t"),
+                  ("size", "int64_t"), ("description", "string")]
+        for k, t in vprops:
+            g.vp[k] = g.new_vp(t)
+        g.ep.type = g.new_ep("string")
+        g.ep.constraint = g.new_ep("string")
+        g.ep.alternatives = g.new_ep("int")
+
+        vmap = {}
+        def add(p):
+            v = g.add_vertex()
+            vmap[p] = int(v)
+            g.vp.name[v] = p
+            return int(v)
+        for p in pkgs:
+            add(p)
+        provided = set()
+        edges = []
+        for p, st in pkgs.items():
+            for k in REL_FIELDS:
+                if k not in st:
+                    continue
+                for alts_ in _relations(st[k]):
+                    for d, c in alts_:
+                        if k == "provides":
+                            provided.add(d)
+                        edges.append((p, d, REL_NAMES[k], c, len(alts_)))
+        kinds = {}
+        for s, t, *_ in edges:
+            if t not in vmap:
+                add(t)
+                kinds[t] = "virtual" if t in provided else "missing"
+
+        def num(x):
+            try:
+                return int(x.split()[0])
+            except (ValueError, IndexError, AttributeError):
+                return -1
+        for p, v in vmap.items():
+            st = pkgs.get(p)
+            if st is None:
+                g.vp.kind[v] = kinds[p]
+                g.vp.installed_size[v] = -1
+                g.vp.size[v] = -1
+                continue
+            g.vp.kind[v] = "package"
+            ver = st.get("version", "")
+            if "revision" in st:
+                ver += "-" + st["revision"]
+            g.vp.version[v] = ver
+            g.vp.component[v] = st["_component"]
+            g.vp.section[v] = st.get("section", "")
+            g.vp.priority[v] = st.get("priority", "")
+            g.vp.essential[v] = st.get("essential", "").lower() == "yes"
+            g.vp.architecture[v] = st.get("architecture", "")
+            src = st.get("source", "").split(" ")[0]
+            g.vp.source[v] = src if src != p else ""
+            g.vp.maintainer[v] = st.get("maintainer", "")
+            g.vp.installed_size[v] = num(st.get("installed-size"))
+            g.vp.size[v] = num(st.get("size"))
+            g.vp.description[v] = st.get("description", "").split("\n")[0].strip()
+
+        g.add_edge_list([(vmap[s], vmap[t], k, c, n) for s, t, k, c, n in edges],
+                        eprops=[g.ep.type, g.ep.constraint, g.ep.alternatives])
+        g.gp.release = g.new_gp("string", rel[1])
+        g.gp.codename = g.new_gp("string", rel[0] if rel[3] is not None else "")
+        g.gp.release_date = g.new_gp("string", rel[2])
+        yield alt, g