Skip to content
Back to skills

Python Advanced Sql

ASecurity

OOP for bioinformatics classes, decorators, context managers, error handling, and SQL queries for biological databases.

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
data-aipythonsqlexpressdatabase

Works with

  • cli

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add Pavel-Kravchenko/Bioinformatics --skill python-advanced-sql --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Python Advanced Sql?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Python Advanced Sql
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/pavel-kravchenko-python-advanced-sql/badge)](https://www.skillsdirectory.com/skills/pavel-kravchenko-python-advanced-sql)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: python-advanced-sql
description: OOP for bioinformatics classes, decorators, context managers, error handling, and SQL queries for biological databases.
tool_type: python
primary_tool: Python
---

# Advanced Python & SQL for Bioinformatics

## When to Use
- Modeling sequences, genes, variants, alignments as Python objects
- Adding caching, validation, timing, or retry logic to pipeline functions
- Safely handling files, DB connections, and temp files via context managers
- Querying biological databases (SQLite, Ensembl MySQL, UCSC) with SQL

## Quick Reference

### OOP Dunders
| Method | Purpose |
|--------|---------|
| `__init__` | Constructor |
| `__str__` / `__repr__` | User / debug string |
| `__len__` | `len(obj)` |
| `__eq__`, `__lt__` | Comparison / sorting |
| `__contains__` | `"ATG" in seq` syntax |
| `__enter__` / `__exit__` | Context manager |

### SQL Clauses
| Clause | Use |
|--------|-----|
| `WHERE biotype = 'protein_coding'` | Filter rows |
| `GROUP BY tissue, condition` | Aggregate groups |
| `HAVING AVG(tpm) > 50` | Filter after grouping |
| `INNER JOIN` | Matching rows only |
| `LEFT JOIN` | All left rows, NULLs for no match |
| Subquery with `IN (SELECT ...)` | Multi-condition filter |


## Key Patterns

### Decorators
```python
import functools, time

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        t = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"[timer] {func.__name__}: {time.perf_counter()-t:.4f}s")
        return result
    return wrapper

def memoize(func):
    cache = {}
    @functools.wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    wrapper.cache = cache
    return wrapper

def validate_sequence(valid_chars: str, seq_type: str = "DNA"):
    valid_set = set(valid_chars.upper())
    def decorator(func):
        @functools.wraps(func)
        def wrapper(seq, *args, **kwargs):
            invalid = set(seq.upper()) - valid_set
            if invalid:
                raise ValueError(f"Invalid {seq_type} characters {invalid} in {func.__name__}()")
            return func(seq, *args, **kwargs)
        return wrapper
    return decorator

def retry(max_attempts: int = 3, delay: float = 0.5):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last_err = None
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_err = e
                    if attempt < max_attempts:
                        time.sleep(delay)
            raise last_err
        return wrapper
    return decorator

# Stacking: applied bottom-up (validate runs first, then timer wraps it)
@timer
@validate_sequence('ATGC', seq_type='DNA')
def analyze(seq: str): ...
```

### Context Managers
```python
class FastaWriter:
    def __init__(self, filename: str, line_width: int = 80):
        self.filename, self.line_width = filename, line_width
        self.file = None

    def __enter__(self):
        self.file = open(self.filename, 'w')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()
        return False  # never suppress exceptions

    def write_record(self, seq_id: str, seq: str, desc: str = ""):
        header = f">{seq_id}" + (f" {desc}" if desc else "")
        self.file.write(header + "\n")
        for i in range(0, len(seq), self.line_width):
            self.file.write(seq[i:i+self.line_width] + "\n")


# contextlib for simple cases
from contextlib import contextmanager
import os, tempfile

@contextmanager
def temp_fasta(sequences: dict[str, str]):
    fd, path = tempfile.mkstemp(suffix='.fasta')
    try:
        with os.fdopen(fd, 'w') as f:
            for sid, seq in sequences.items():
                f.write(f">{sid}\n{seq}\n")
        yield path
    finally:
        os.unlink(path)
```

### GeneAnnotation Dataclass
```python
from dataclasses import dataclass, field

@dataclass(order=True)
class GeneAnnotation:
    chromosome: str
    start: int
    end: int
    name: str      = field(compare=False, default="")
    strand: str    = field(compare=False, default='+')
    gene_type: str = field(compare=False, default="protein_coding")

    @property
    def length(self): return self.end - self.start

    def overlaps(self, other: "GeneAnnotation") -> bool:
        return (self.chromosome == other.chromosome
                and self.start < other.end
                and other.start < self.end)
```

### SQL — SQLite Setup
```python
import sqlite3, pandas as pd

conn = sqlite3.connect(':memory:')
conn.executescript('''
    CREATE TABLE genes (
        gene_id INTEGER PRIMARY KEY, symbol TEXT, chromosome TEXT,
        start_pos INTEGER, end_pos INTEGER, strand TEXT, biotype TEXT
    );
    CREATE TABLE expression (
        expr_id INTEGER PRIMARY KEY, gene_id INTEGER REFERENCES genes(gene_id),
        sample_id TEXT, tissue TEXT, tpm REAL, condition TEXT
    );
    CREATE TABLE variants (
        variant_id INTEGER PRIMARY KEY, gene_id INTEGER REFERENCES genes(gene_id),
        position INTEGER, ref_allele TEXT, alt_allele TEXT, clinical_significance TEXT
    );
''')
```

### SQL — Common Bio Queries
```python
# Gene lengths > 100 kb
pd.read_sql_query("""
    SELECT symbol, (end_pos - start_pos) AS length
    FROM genes WHERE (end_pos - start_pos) > 100000
    ORDER BY length DESC
""", conn)

# Avg expression per tissue/condition
pd.read_sql_query("""
    SELECT tissue, condition, ROUND(AVG(tpm),2) AS avg_tpm, COUNT(*) AS n
    FROM expression GROUP BY tissue, condition
""", conn)

# Top tumor-expressed genes (HAVING filters aggregated groups)
pd.read_sql_query("""
    SELECT g.symbol, ROUND(AVG(e.tpm),2) AS avg_tumor_tpm
    FROM genes g JOIN expression e ON g.gene_id = e.gene_id
    WHERE e.condition = 'tumor'
    GROUP BY g.symbol HAVING AVG(e.tpm) > 50
    ORDER BY avg_tumor_tpm DESC
""", conn)

# All genes with variant count including 0s (LEFT JOIN)
pd.read_sql_query("""
    SELECT g.symbol, COUNT(v.variant_id) AS n_variants
    FROM genes g LEFT JOIN variants v ON g.gene_id = v.gene_id
    GROUP BY g.symbol ORDER BY n_variants DESC
""", conn)

# Genes highly expressed in tumors AND carrying pathogenic variants
pd.read_sql_query("""
    SELECT symbol FROM genes
    WHERE gene_id IN (
        SELECT gene_id FROM expression WHERE condition='tumor'
        GROUP BY gene_id HAVING AVG(tpm) > 50
    ) AND gene_id IN (
        SELECT gene_id FROM variants WHERE clinical_significance='pathogenic'
    )
""", conn)
```


## Pitfalls

- **Missing `@functools.wraps`**: decorated function loses `__name__` and `__doc__`, breaking introspection and logging.
- **Bare `except:`**: catches `SystemExit` and `KeyboardInterrupt`; always catch specific types.
- **`__exit__` returning `True`**: suppresses all exceptions silently — only do this intentionally.
- **`@lru_cache` on methods**: caches `self`, leaking instances; use on module-level or static functions only.
- **Stacking decorators**: applied bottom-up — `@timer` wraps `@validate_sequence`, so validation runs first.
- **SQL `HAVING` vs `WHERE`**: `WHERE` filters rows before grouping; `HAVING` filters after aggregation.
- **`LEFT JOIN` count**: use `COUNT(v.variant_id)` not `COUNT(*)` to count non-NULL matches.
- **`raise ... from e`**: preserves original traceback; omitting `from e` hides the root cause.
- **Properties without `_` backing attribute**: `self.sequence = value` inside a `sequence` setter causes infinite recursion; use `self._sequence`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…