< Summary - Repometa Coverage

Information
Class: pythonDetector
Assembly: repometa
File(s): /home/runner/work/repometa/repometa/detect_python.go
Line coverage
87%
Covered lines: 43
Uncovered lines: 6
Coverable lines: 49
Total lines: 99
Line coverage: 87.7%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
detect0%0087.76%

File(s)

/home/runner/work/repometa/repometa/detect_python.go

#LineLine coverage
 1package repometa
 2
 3import (
 4  "io/fs"
 5  "path/filepath"
 6
 7  "github.com/BurntSushi/toml"
 8)
 9
 10type pythonDetector struct{}
 11
 12type pyprojectTOML struct {
 13  Tool struct {
 14    Uv *struct {
 15      Workspace *struct {
 16        Members []string `toml:"members"`
 17      } `toml:"workspace"`
 18    } `toml:"uv"`
 19    Poetry *struct{} `toml:"poetry"`
 20  } `toml:"tool"`
 21  Project *struct {
 22    Name string `toml:"name"`
 23  } `toml:"project"`
 24}
 25
 8426func (pythonDetector) detect(dv dirVisit, cfg options) []finding {
 8427  // Any of these markers makes the directory a Python package for our
 8428  // purposes. pyproject.toml wins the "primary" evidence slot when
 8429  // present because it is the modern canonical form.
 8430  markers := []string{"pyproject.toml", "setup.py", "setup.cfg", "requirements.txt", "Pipfile"}
 8431  primary, ok := firstFile(dv.files, markers...)
 7432  if !ok {
 7433    return nil
 7434  }
 35
 1036  attrs := detectPythonPM(dv.files)
 037  if attrs == nil {
 038    attrs = map[string]string{}
 039  }
 1040  evidence := []Evidence{{Path: relJoin(dv.rel, primary), Reason: "Python packaging marker"}}
 1041  var workspaces []Workspace
 1042
 1043  confidence := 1.0
 844  if primary == "pyproject.toml" {
 845    data, err := readManifest(filepath.Join(dv.abs, "pyproject.toml"), cfg)
 846    switch {
 047    case err != nil:
 048      confidence = confidenceUnreadable
 049      evidence = append(evidence, evidenceUnreadable(dv.rel, "pyproject.toml", err))
 850    default:
 851      var pp pyprojectTOML
 152      if uerr := toml.Unmarshal(data, &pp); uerr != nil {
 153        confidence = confidenceUnparsable
 154        evidence = append(evidence, evidenceUnparsable(dv.rel, "pyproject.toml", uerr))
 155        break
 56      }
 157      if pp.Tool.Uv != nil && pp.Tool.Uv.Workspace != nil {
 158        members := expandMembers(dv.abs, pp.Tool.Uv.Workspace.Members, dv.rel)
 159        workspaces = append(workspaces, Workspace{Kind: WorkspaceUv, Members: members})
 160        evidence = append(evidence, Evidence{
 161          Path:   relJoin(dv.rel, "pyproject.toml"),
 162          Reason: "pyproject.toml declares [tool.uv.workspace]",
 163        })
 164      }
 165      if pp.Tool.Poetry != nil {
 166        // Poetry is not a workspace tool in v0 — recorded as
 167        // package-manager attribute only.
 168        attrs["python.pm"] = "poetry"
 169      }
 70    }
 71  }
 72
 1073  return []finding{{
 1074    Kind:       KindPythonPackage,
 1075    Confidence: confidence,
 1076    Evidence:   evidence,
 1077    Workspaces: workspaces,
 1078    Attributes: attrs,
 1079  }}
 80}
 81
 82// detectPythonPM returns the most specific package-manager attribution
 83// available from lockfiles / marker files in the directory. Order reflects
 84// modern preference: uv > poetry > pipenv > pip. The result may be empty
 85// when no lockfile or requirements.txt is present.
 86func detectPythonPM(files []fs.DirEntry) map[string]string {
 87  attrs := map[string]string{}
 88  switch {
 89  case hasFile(files, "uv.lock"):
 90    attrs["python.pm"] = "uv"
 91  case hasFile(files, "poetry.lock"):
 92    attrs["python.pm"] = "poetry"
 93  case hasFile(files, "Pipfile.lock") || hasFile(files, "Pipfile"):
 94    attrs["python.pm"] = "pipenv"
 95  case hasFile(files, "requirements.txt"):
 96    attrs["python.pm"] = "pip"
 97  }
 98  return attrs
 99}

Methods/Properties

detect