Skip to content

Commit d4cc8f5

Browse files
committed
clean up python scripts
1 parent 66ba8b5 commit d4cc8f5

5 files changed

Lines changed: 40 additions & 31 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ markeR.Rproj
1818
inst/doc
1919
data_aux
2020
markeR.Rcheck
21+
/python/.venv

python/README.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,20 @@ Bioconductor R package **markeR** via `rpy2`.
1212

1313
## Prerequisites
1414

15-
* R (>=.4.5) installed and on your `PATH`.
16-
* A Python virtual environment. Install dependencies with:
17-
18-
```bash
19-
python -m venv .venv
20-
source .venv/bin/activate
21-
pip install -r requirements.txt
22-
```
15+
* R (>=4.5) installed and on your `PATH`.
16+
* A Python virtual environment. A `requirements.txt` file is provided in
17+
this folder listing the needed packages (`rpy2`, `pandas`, `numpy` plus
18+
optional `ipython`/`jupyter` for notebook usage).
19+
To set up the environment:
20+
21+
```bash
22+
python -m venv .venv
23+
source .venv/bin/activate
24+
pip install -r requirements.txt
25+
```
26+
27+
After activation you can run the helper scripts using `python` from the
28+
same environment.
2329

2430
## Quick start
2531

python/markeR_to_python.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,6 @@
6060
# converting. helpers below wrap the recommended API.
6161

6262

63-
def _to_r(obj):
64-
"""Convert a pandas object to an R object using the current converter."""
65-
with conversion.localconverter(ro.default_converter + pandas2ri.converter):
66-
return conversion.py2rpy(obj)
67-
68-
6963
def _to_py(obj):
7064
"""Convert an R object to a pandas/numpy equivalent."""
7165
with conversion.localconverter(ro.default_converter + pandas2ri.converter):
@@ -175,7 +169,6 @@ def plot_r_function(func_name: str, *args, width=800, height=600, filename=None,
175169

176170
def ensure_bioc_installed() -> None:
177171
"""Install Bioconductor's package manager if it is not already present."""
178-
utils = importr("utils")
179172
biocinstaller = "BiocManager"
180173
if not isinstalled(biocinstaller):
181174
ro.r('install.packages("{0}")'.format(biocinstaller))
@@ -189,22 +182,17 @@ def install_markeR() -> None:
189182
the package should be loadable via `importr("markeR")`.
190183
"""
191184
ensure_bioc_installed()
192-
# use importr to check presence rather than only the isinstalled helper
193-
try:
194-
importr("markeR")
195-
except Exception:
196-
# attempt installation if import failed
185+
if not isinstalled("markeR"):
197186
ro.r('BiocManager::install("markeR", ask=FALSE, update=FALSE)')
198-
# load into namespace for side effects
199-
ro.r('library(markeR)')
187+
ro.r('library(markeR)')
200188

201189

202190
def get_markeR_functions() -> ro.Environment:
203191
"""Return the markeR namespace so that functions can be accessed conveniently.
204192
205193
Example:
206194
mark = get_markeR_functions()
207-
imputed = mark.rgImpute(...)
195+
scores = mark.CalculateScores(data=counts, metadata=metadata, gene_sets=genesets, method="logmedian")
208196
"""
209197
install_markeR()
210198
# importing via importr is more reliable than accessing `ro.r['markeR']`.
@@ -330,4 +318,4 @@ def tutorial_benchmark(output_file=None):
330318
print(" example: python markeR_to_python.py --tutorial --output my_plot.png")
331319
else:
332320
print("usage: python markeR_to_python.py --tutorial [--output FILENAME]")
333-
print("See the module docstring for more details.")
321+
print("See the module docstring for more details.")

python/requirements.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# install packages for markeR_to_python script:
2+
# pip install -r requirements.txt
3+
4+
rpy2>=3.6
5+
pandas
6+
numpy
7+
# optional (for notebook inline display)
8+
ipython
9+
jupyter

python/run_marker_function.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import argparse
2626
import json
2727
import os
28+
import re
2829

2930
# Check dependencies
3031
_missing = []
@@ -42,7 +43,6 @@
4243

4344
def ensure_bioc_installed() -> None:
4445
"""Install Bioconductor's package manager if it is not already present."""
45-
utils = importr("utils")
4646
biocinstaller = "BiocManager"
4747
if not isinstalled(biocinstaller):
4848
ro.r('install.packages("{0}")'.format(biocinstaller))
@@ -52,11 +52,9 @@ def ensure_bioc_installed() -> None:
5252
def install_markeR() -> None:
5353
"""Install the markeR package from Bioconductor if not already installed."""
5454
ensure_bioc_installed()
55-
try:
56-
importr("markeR")
57-
except Exception:
55+
if not isinstalled("markeR"):
5856
ro.r('BiocManager::install("markeR", ask=FALSE, update=FALSE)')
59-
ro.r('library(markeR)')
57+
ro.r('library(markeR)')
6058

6159

6260
def load_example_data():
@@ -113,7 +111,8 @@ def parse_parameter(value: str):
113111
except (json.JSONDecodeError, ValueError):
114112
pass
115113

116-
# Default: treat as string
114+
# Default: treat as string, escaping any internal quotes
115+
value = value.replace('"', '\\"')
117116
return f'"{value}"'
118117

119118

@@ -272,6 +271,12 @@ def main():
272271
return
273272

274273
func_name = sys.argv[1]
274+
275+
# Validate function name to prevent code injection
276+
if not re.match(r'^[A-Za-z][A-Za-z0-9_.]*$', func_name):
277+
sys.exit(f"Error: invalid function name '{func_name}'. "
278+
"Function names must start with a letter and contain only letters, digits, dots or underscores.")
279+
275280
output_file = None
276281
width = 800
277282
height = 600
@@ -358,4 +363,4 @@ def main():
358363

359364

360365
if __name__ == "__main__":
361-
main()
366+
main()

0 commit comments

Comments
 (0)