Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Testing
.pytest_cache/
.coverage
htmlcov/
coverage.xml
.tox/
.nox/

# Claude Code settings
.claude/*

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Virtual environments
.virtualenv/
.venv/
venv/
ENV/

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Logs
*.log

# Model files (typically large)
*.bin
*.safetensors
*.h5

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# pipenv
Pipfile.lock

# poetry
# Uncomment if you want to ignore poetry.lock
# poetry.lock

# PEP 582
__pypackages__/

# Celery
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/
Empty file added chemdfm/__init__.py
Empty file.
2,409 changes: 2,409 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

75 changes: 75 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
[tool.poetry]
name = "chemdfm"
version = "1.5.0"
description = "ChemDFM - Chemical Domain Foundation Model"
authors = ["ChemDFM Team"]
readme = "README.md"
packages = [{include = "chemdfm", from = "."}]

[tool.poetry.dependencies]
python = "^3.8"
torch = "^2.0.0"
transformers = "^4.30.0"

[tool.poetry.group.test.dependencies]
pytest = "^7.4.0"
pytest-cov = "^4.1.0"
pytest-mock = "^3.11.0"

# Poetry run commands are configured in [tool.poetry] section
# Use: poetry run pytest

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"--strict-config",
"--cov=chemdfm",
"--cov-report=html:htmlcov",
"--cov-report=xml:coverage.xml",
"--cov-report=term-missing",
"--cov-fail-under=80",
"-v",
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Tests that take a long time to run",
]

[tool.coverage.run]
source = ["chemdfm"]
branch = true
omit = [
"tests/*",
"*/conftest.py",
"*/__init__.py",
"*/migrations/*",
"*/venv/*",
"*/.venv/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]
show_missing = true
precision = 2

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"
Empty file added tests/__init__.py
Empty file.
116 changes: 116 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""Shared pytest fixtures and configuration."""

import os
import tempfile
import shutil
from pathlib import Path
from unittest.mock import Mock, MagicMock
import pytest


@pytest.fixture
def temp_dir():
"""Create a temporary directory for test files."""
temp_path = tempfile.mkdtemp()
yield Path(temp_path)
shutil.rmtree(temp_path)


@pytest.fixture
def mock_tokenizer():
"""Mock tokenizer for testing without loading actual model."""
tokenizer = Mock()
tokenizer.from_pretrained.return_value = tokenizer
tokenizer.batch_decode.return_value = ["test response"]
tokenizer.eos_token_id = 1
tokenizer.return_value = Mock(input_ids="mock_input_ids", to=Mock(return_value=Mock(input_ids="mock_input_ids")))
return tokenizer


@pytest.fixture
def mock_model():
"""Mock model for testing without loading actual model."""
model = Mock()
model.from_pretrained.return_value = model
model.generate.return_value = "mock_generated_ids"
model.eval.return_value = None
return model


@pytest.fixture
def mock_torch():
"""Mock torch functionality."""
torch_mock = Mock()
torch_mock.cuda.is_available.return_value = False
torch_mock.float16 = "float16"
return torch_mock


@pytest.fixture
def sample_args():
"""Sample argument configuration for testing."""
args = Mock()
args.model_name_or_path = "test_model"
args.max_new_tokens = 100
args.do_sample = True
args.temperature = 0.7
args.top_k = 50
args.top_p = 0.9
args.num_beams = 1
args.num_beam_groups = 1
args.repetition_penalty = 1.0
args.max_round = 5
return args


@pytest.fixture
def mock_config():
"""Mock configuration for various test scenarios."""
config = {
"model_path": "test/model/path",
"device": "cpu",
"max_tokens": 512,
"temperature": 0.1
}
return config


@pytest.fixture(scope="session")
def test_data_dir():
"""Path to test data directory."""
return Path(__file__).parent / "data"


@pytest.fixture
def capture_stdout(monkeypatch):
"""Fixture to capture stdout for testing print statements."""
import io
import sys

captured_output = io.StringIO()
monkeypatch.setattr(sys, 'stdout', captured_output)
return captured_output


@pytest.fixture
def mock_input(monkeypatch):
"""Mock input function for interactive testing."""
inputs = []

def mock_input_func(prompt=""):
if inputs:
return inputs.pop(0)
return "/clear"

monkeypatch.setattr("builtins.input", mock_input_func)
return inputs


@pytest.fixture
def clean_environment(monkeypatch):
"""Clean environment variables for testing."""
test_env = {}
for key in os.environ:
if not key.startswith("PYTEST"):
monkeypatch.delenv(key, raising=False)
return test_env
Empty file added tests/integration/__init__.py
Empty file.
Loading