File: python-tools.R

package info (click to toggle)
r-cran-reticulate 1.41.0.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,088 kB
  • sloc: cpp: 5,154; python: 620; sh: 13; makefile: 2
file content (166 lines) | stat: -rw-r--r-- 4,527 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166

python_has_modules <- function(python, modules) {

  # write code to tempfile
  file <- tempfile("reticulate-python-", fileext = ".py")
  code <- paste("import", modules)
  writeLines(code, con = file)
  on.exit(unlink(file), add = TRUE)

  # invoke Python
  status <- system2(python, shQuote(file), stdout = FALSE, stderr = FALSE)
  status == 0L

}

python_has_module <- function(python, module) {
  code <- paste("import", module)
  args <- c("-E", "-c", shQuote(code))
  status <- system2(python, args, stdout = FALSE, stderr = FALSE)
  status == 0L
}

python_version <- function(python) {
  code <- "import platform; print(platform.python_version())"
  args <- c("-E", "-c", shQuote(code))
  output <- system2(python, args, stdout = TRUE, stderr = FALSE)
  sanitized <- gsub("[^0-9.-]", "", output)
  numeric_version(sanitized)
}

python_module_version <- function(python, module) {
  fmt <- "import %1$s; print(%1$s.__version__)"
  code <- sprintf(fmt, module)
  args <- c("-E", "-c", shQuote(code))
  output <- system2(python, args, stdout = TRUE, stderr = FALSE)
  numeric_version(output)
}

# given the path to a python binary, or an environment path,
# try to find the path to the associated python binary, and
# figure out if it's a virtualenv, conda environment, or none
python_info <- function(path) {

  path <- path.expand(path)
  parent <- dirname(path)

  # NOTE: we check for both 'python' and 'python3' because certain python
  # installations might install one version of the binary but not the other.
  #
  # Some installations might not place Python within a 'Scripts' or 'bin'
  # sub-directory, so look in the root directory too.
  prefixes <- list(NULL, if (is_windows()) "Scripts" else "bin")
  suffixes <- if (is_windows())
    "python.exe"
  else if (startsWith(basename(path), "python3"))
    "python3" # don't resolve 'python' for 'python3'
  else
    c("python", "python3")

  # placeholder for a discovered system python
  systemPython <- NULL

  while (path != parent) {

    # check for virtual environment files
    files <- c(
      "pyvenv.cfg",                                  # created by venv
      file.path(prefixes[[2L]], "activate_this.py")  # created by virtualenv
    )

    paths <- file.path(path, files)
    virtualenv <- any(file.exists(paths))

    # extra check that we aren't in a conda environment
    condapath <- file.path(path, "condabin/conda")
    if (file.exists(condapath))
      virtualenv <- FALSE

    if (virtualenv)
      return(python_info_virtualenv(path))

    # check for conda environment files
    condaenv <- file.exists(file.path(path, "conda-meta"))
    if (condaenv)
      return(python_info_condaenv(path))

    # check for python binary (implies a system install)
    # we don't return immediately here because we might find
    # as we traverse upwards that some of the expected virtualenv
    # or condaenv files exist, so we just save the path and use
    # it later if appropriate
    if (is.null(systemPython)) {
      for (prefix in prefixes) {
        for (suffix in suffixes) {
          bin <- paste(c(path, prefix, suffix), collapse = "/")
          if (file.exists(bin)) {
            systemPython <- bin
            break
          }
        }
      }
    }

    # recurse
    parent <- path
    path <- dirname(path)

  }

  # if we found a system python, use that as the fallback
  if (!is.null(systemPython))
    return(python_info_system(dirname(systemPython), systemPython))

  stopf("could not find a Python environment for %s", path)

}

python_info_virtualenv <- function(path) {

  # form path to python binary
  suffix <- if (is_windows()) "Scripts/python.exe" else "bin/python"
  python <- file.path(path, suffix)

  # return details
  out <- list(
    python = python,
    type = "virtualenv",
    root = path
  )

  if (file.exists(cfg <- file.path(out$root, "pyvenv.cfg"))) {
    starter <- grep("^home = ", readLines(cfg), value = TRUE)
    if(length(starter))
      out$starter <- str_drop_prefix(starter, "home = ")
  }

  out
}

python_info_condaenv <- function(path) {

  # form path to python binary
  suffix <- if (is_windows()) "python.exe" else "bin/python"
  python <- file.path(path, suffix)

  # find path to conda associated with this env
  conda <- get_python_conda_info(python)$conda

  list(
    python = python,
    type   = "conda",
    root   = path,
    conda  = conda
  )

}

python_info_system <- function(path, python) {

  list(
    python = python,
    type   = "system",
    root   = path
  )

}