diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13bcbef..856b82c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,11 @@ jobs: - conda-forge - defaults + - name: Install package (editable) + shell: micromamba-shell {0} + run: | + python -m pip install -e . --no-deps + - name: Run smoke tests shell: micromamba-shell {0} run: | diff --git a/README.md b/README.md index 92afa53..75a0f84 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,12 @@ cd ResearchCodes ### 2) Create the conda environment from `environment.yml` -Create a fresh environment: +Create a fresh environment and install `researchcodes` in editable mode: ```bash conda env create -f environment.yml conda activate researchcodes +python -m pip install -e . --no-deps ``` Notes: - `environment.yml` is the source of truth for dependencies. diff --git a/Projects/Photoz/Compile_Standard_Star_Catalog/APASS/APASSDR10.h5 b/docs/tutorials/Compile_Standard_Star_Catalog/APASS/APASSDR10.h5 similarity index 99% rename from Projects/Photoz/Compile_Standard_Star_Catalog/APASS/APASSDR10.h5 rename to docs/tutorials/Compile_Standard_Star_Catalog/APASS/APASSDR10.h5 index 6a90c8a..e30fc61 100644 Binary files a/Projects/Photoz/Compile_Standard_Star_Catalog/APASS/APASSDR10.h5 and b/docs/tutorials/Compile_Standard_Star_Catalog/APASS/APASSDR10.h5 differ diff --git a/Projects/Photoz/Compile_Standard_Star_Catalog/APASS/Compile_APASS.ipynb b/docs/tutorials/Compile_Standard_Star_Catalog/APASS/Compile_APASS.ipynb similarity index 69% rename from Projects/Photoz/Compile_Standard_Star_Catalog/APASS/Compile_APASS.ipynb rename to docs/tutorials/Compile_Standard_Star_Catalog/APASS/Compile_APASS.ipynb index c6600b0..283d4b7 100644 --- a/Projects/Photoz/Compile_Standard_Star_Catalog/APASS/Compile_APASS.ipynb +++ b/docs/tutorials/Compile_Standard_Star_Catalog/APASS/Compile_APASS.ipynb @@ -11,19 +11,11 @@ "source": [ "import sys\n", "from pathlib import Path\n", - "\n", - "# set up the system path to import the \n", - "# compile_standard_catalog module \n", - "extra = Path(\"..\").resolve()\n", - "sys.path.insert(0, str(extra))\n", - "\n", - "from compile_standard_catalog import (\n", + "from researchcodes import (\n", " define_column_desc, \n", " iter_multi_csv_chunks, \n", " write_std_h5, \n", - ")\n", - "\n", - "\n" + ")" ] }, { @@ -36,18 +28,29 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 12, "id": "ded1390c-30c1-439a-a321-b0194e3591ff", "metadata": { "tags": [] }, "outputs": [], "source": [ - "# define magnitude column names\n", + "# Define magnitude column names\n", + "\n", + "# Note this magnitude column order is not necessarily\n", + "# to be the same as the column order in the text file. \n", + "\n", + "# It is OK as long as the filter names in \n", + "# `magnitude_column_names` matches `colnames` defined \n", + "# when reading the text file\n", + "\n", "magnitude_column_names = [\n", - " \"mag_B\",\"mag_V\",\"mag_u\",\"mag_g\",\"mag_r\",\"mag_i\",\"mag_PanSTARRS_zs\",\"mag_PanSTARRS_Y\",\n", - " # emag 8个\n", - " \"emag_B\",\"emag_V\",\"emag_u\",\"emag_g\",\"emag_r\",\"emag_i\",\"emag_PanSTARRS_zs\",\"emag_PanSTARRS_Y\",\n", + " \"Johnson_B\", \"Johnson_V\",\n", + " \"SDSS_up\", \"SDSS_gp\", \"SDSS_rp\", \"SDSS_ip\",\n", + " \"PanSTARRS_zs\",\"PanSTARRS_Y\",\n", + " \"Johnson_B_err\", \"Johnson_V_err\",\n", + " \"SDSS_up_err\", \"SDSS_gp_err\", \"SDSS_rp_err\", \"SDSS_ip_err\",\n", + " \"PanSTARRS_zs_err\",\"PanSTARRS_Y_err\",\n", "]\n", "\n", "# define h5 file column description\n", @@ -71,22 +74,22 @@ " 'ra_err': Float32Col(shape=(), dflt=np.float32(0.0), pos=2),\n", " 'dec': Float32Col(shape=(), dflt=np.float32(0.0), pos=3),\n", " 'dec_err': Float32Col(shape=(), dflt=np.float32(0.0), pos=4),\n", - " 'mag_B': Float32Col(shape=(), dflt=np.float32(0.0), pos=5),\n", - " 'mag_V': Float32Col(shape=(), dflt=np.float32(0.0), pos=6),\n", - " 'mag_u': Float32Col(shape=(), dflt=np.float32(0.0), pos=7),\n", - " 'mag_g': Float32Col(shape=(), dflt=np.float32(0.0), pos=8),\n", - " 'mag_r': Float32Col(shape=(), dflt=np.float32(0.0), pos=9),\n", - " 'mag_i': Float32Col(shape=(), dflt=np.float32(0.0), pos=10),\n", - " 'mag_PanSTARRS_zs': Float32Col(shape=(), dflt=np.float32(0.0), pos=11),\n", - " 'mag_PanSTARRS_Y': Float32Col(shape=(), dflt=np.float32(0.0), pos=12),\n", - " 'emag_B': Float32Col(shape=(), dflt=np.float32(0.0), pos=13),\n", - " 'emag_V': Float32Col(shape=(), dflt=np.float32(0.0), pos=14),\n", - " 'emag_u': Float32Col(shape=(), dflt=np.float32(0.0), pos=15),\n", - " 'emag_g': Float32Col(shape=(), dflt=np.float32(0.0), pos=16),\n", - " 'emag_r': Float32Col(shape=(), dflt=np.float32(0.0), pos=17),\n", - " 'emag_i': Float32Col(shape=(), dflt=np.float32(0.0), pos=18),\n", - " 'emag_PanSTARRS_zs': Float32Col(shape=(), dflt=np.float32(0.0), pos=19),\n", - " 'emag_PanSTARRS_Y': Float32Col(shape=(), dflt=np.float32(0.0), pos=20),\n", + " 'Johnson_B': Float32Col(shape=(), dflt=np.float32(0.0), pos=5),\n", + " 'Johnson_V': Float32Col(shape=(), dflt=np.float32(0.0), pos=6),\n", + " 'SDSS_up': Float32Col(shape=(), dflt=np.float32(0.0), pos=7),\n", + " 'SDSS_gp': Float32Col(shape=(), dflt=np.float32(0.0), pos=8),\n", + " 'SDSS_rp': Float32Col(shape=(), dflt=np.float32(0.0), pos=9),\n", + " 'SDSS_ip': Float32Col(shape=(), dflt=np.float32(0.0), pos=10),\n", + " 'PanSTARRS_zs': Float32Col(shape=(), dflt=np.float32(0.0), pos=11),\n", + " 'PanSTARRS_Y': Float32Col(shape=(), dflt=np.float32(0.0), pos=12),\n", + " 'Johnson_B_err': Float32Col(shape=(), dflt=np.float32(0.0), pos=13),\n", + " 'Johnson_V_err': Float32Col(shape=(), dflt=np.float32(0.0), pos=14),\n", + " 'SDSS_up_err': Float32Col(shape=(), dflt=np.float32(0.0), pos=15),\n", + " 'SDSS_gp_err': Float32Col(shape=(), dflt=np.float32(0.0), pos=16),\n", + " 'SDSS_rp_err': Float32Col(shape=(), dflt=np.float32(0.0), pos=17),\n", + " 'SDSS_ip_err': Float32Col(shape=(), dflt=np.float32(0.0), pos=18),\n", + " 'PanSTARRS_zs_err': Float32Col(shape=(), dflt=np.float32(0.0), pos=19),\n", + " 'PanSTARRS_Y_err': Float32Col(shape=(), dflt=np.float32(0.0), pos=20),\n", " 'ipix': Int32Col(shape=(), dflt=np.int32(0), pos=21),\n", " 'bucket': Int32Col(shape=(), dflt=np.int32(0), pos=22)}" ] @@ -125,7 +128,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "id": "416165d4-bb8c-466b-abe8-c85f02114537", "metadata": { "tags": [] @@ -135,12 +138,13 @@ "# csv column names\n", "colnames = [\n", " \"id_name\", \"ra\", \"ra_err\", \"dec\", \"dec_err\",\n", - " # nobs 8个\n", " \"nobs_B\",\"nobs_V\",\"nobs_u\",\"nobs_g\",\"nobs_r\",\"nobs_i\",\"nobs_PanSTARRS_zs\",\"nobs_PanSTARRS_Y\",\n", - " # mag 8个\n", - " \"mag_B\",\"mag_V\",\"mag_u\",\"mag_g\",\"mag_r\",\"mag_i\",\"mag_PanSTARRS_zs\",\"mag_PanSTARRS_Y\",\n", - " # emag 8个\n", - " \"emag_B\",\"emag_V\",\"emag_u\",\"emag_g\",\"emag_r\",\"emag_i\",\"emag_PanSTARRS_zs\",\"emag_PanSTARRS_Y\",\n", + " \"Johnson_B\", \"Johnson_V\",\n", + " \"SDSS_up\", \"SDSS_gp\", \"SDSS_rp\", \"SDSS_ip\",\n", + " \"PanSTARRS_zs\",\"PanSTARRS_Y\",\n", + " \"Johnson_B_err\", \"Johnson_V_err\",\n", + " \"SDSS_up_err\", \"SDSS_gp_err\", \"SDSS_rp_err\", \"SDSS_ip_err\",\n", + " \"PanSTARRS_zs_err\",\"PanSTARRS_Y_err\",\n", "]\n", "\n", "dataframe_iterator = iter_multi_csv_chunks(\n", @@ -158,7 +162,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "id": "2349f2b6-cae4-4eee-a4f1-cc033080993e", "metadata": { "tags": [] @@ -173,7 +177,7 @@ " \"version\": \"dr10\",\n", " \"mag_system\": {\n", " \"Johnson_BV\": \"Vega\", \n", - " \"SDSS_ugri\": \"AB\", \n", + " \"SDSS_ugri_primed\": \"AB\", \n", " \"PanSTARRS_zsY\": \"AB\", \n", " }, \n", "}" @@ -181,7 +185,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "id": "01200976-6664-47af-8868-a6c75ed283ec", "metadata": { "tags": [] @@ -190,7 +194,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "1145d58e3b024d8ab904ee67ff969314", + "model_id": "3b1bbb836bc14e329189cd3d4cfd4720", "version_major": 2, "version_minor": 0 }, @@ -204,12 +208,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "a660e868200b4d369719f07206757394", + "model_id": "de28e8dc18aa44a3bc0fda04c0fc3596", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Chunks: 0%| …" + "Chunks: 0%| | 0/777 [00:00=3.11,<3.12" + + +# Pytest configuration stored in pyproject.toml. +# This section is equivalent to settings in pytest.ini / [tool:pytest] in setup.cfg. [tool.pytest.ini_options] + +# Require at least pytest 6.0; pytest will error out with a helpful message if an older version is used. minversion = "6.0" +# Register custom markers used in this test suite to document them and avoid "unknown marker" warnings. markers = [ "smoke: fast checks to run before push", ] + +# Default command-line options to pass to pytest every time it runs. +# -ra: show a short summary for all test outcomes (especially skipped/failed/xfailed, etc.). addopts = "-ra" + +# Directories where pytest should look for tests when no paths are given on the command line. testpaths = ["tests"] + +# ----------------------------- +# Build system configuration (PEP 517/518) +# ----------------------------- +[build-system] +# Build-time requirements: tools that must be installed in the isolated build environment +# before building a wheel/sdist. Here we require setuptools 68+. +requires = ["setuptools>=68"] + +# The PEP 517 build backend that will perform the build. +# "setuptools.build_meta" is setuptools' official backend for PEP 517 builds. +build-backend = "setuptools.build_meta" + +# ----------------------------- +# Setuptools configuration (src layout) +# ----------------------------- +[tool.setuptools] +# Tell setuptools that the "root package directory" is the "src/" folder. +# In other words, packages are not located at the repository root, but under src/. +package-dir = { "" = "src" } + +[tool.setuptools.packages.find] +# Enable automatic package discovery and restrict the search path to "src/". +# Setuptools will scan src/ and include any Python packages it finds there. +where = ["src"] diff --git a/src/researchcodes/__init__.py b/src/researchcodes/__init__.py new file mode 100644 index 0000000..02ee577 --- /dev/null +++ b/src/researchcodes/__init__.py @@ -0,0 +1,16 @@ +from importlib.metadata import version as _version +__version__ = _version("researchcodes") + +from .photoz import ( + define_column_desc, + iter_multi_csv_chunks, + write_std_h5, + SpecResults, +) + +__all__ = [ + "define_column_desc", + "iter_multi_csv_chunks", + "write_std_h5", + "SpecResults", +] diff --git a/src/researchcodes/photoz/__init__.py b/src/researchcodes/photoz/__init__.py new file mode 100644 index 0000000..db5857a --- /dev/null +++ b/src/researchcodes/photoz/__init__.py @@ -0,0 +1,14 @@ +from .compile_standard_stars import ( + define_column_desc, + iter_multi_csv_chunks, + write_std_h5, +) + +from .plot_spec_file import SpecResults + +__all__ = [ + "define_column_desc", + "iter_multi_csv_chunks", + "write_std_h5", + "SpecResults", +] diff --git a/Projects/Photoz/Compile_Standard_Star_Catalog/compile_standard_catalog.py b/src/researchcodes/photoz/compile_standard_stars.py similarity index 100% rename from Projects/Photoz/Compile_Standard_Star_Catalog/compile_standard_catalog.py rename to src/researchcodes/photoz/compile_standard_stars.py diff --git a/Projects/Photoz/Plot_Spec_File/plot_spec_file.py b/src/researchcodes/photoz/plot_spec_file.py similarity index 100% rename from Projects/Photoz/Plot_Spec_File/plot_spec_file.py rename to src/researchcodes/photoz/plot_spec_file.py diff --git a/tests/test_smoke_imports.py b/tests/test_smoke_imports.py index d0962b6..bf4e8ac 100644 --- a/tests/test_smoke_imports.py +++ b/tests/test_smoke_imports.py @@ -1,55 +1,8 @@ -from __future__ import annotations - -import importlib.util -import sys -from pathlib import Path - import pytest -REPO_ROOT = Path(__file__).resolve().parents[1] -PROJECTS_ROOT = REPO_ROOT / "Projects" - -# 你可以按需要继续加 -SKIP_DIRS = { - "__pycache__", - ".ipynb_checkpoints", - ".pytest_cache", - ".mypy_cache", -} @pytest.mark.smoke -def test_import_all_py_files_under_projects(): - assert PROJECTS_ROOT.exists(), f"Expected folder not found: {PROJECTS_ROOT}" - - # 让 Projects 内部如果有绝对导入(比如 import some_utils)更容易找到 - sys.path.insert(0, str(PROJECTS_ROOT)) - sys.path.insert(0, str(REPO_ROOT)) - - py_files = [] - for p in PROJECTS_ROOT.rglob("*.py"): - parts = set(p.relative_to(PROJECTS_ROOT).parts) - if parts & SKIP_DIRS: - continue - py_files.append(p) - - failures = [] - for p in sorted(py_files): - # 给每个文件一个唯一的“假模块名”,避免冲突 - mod_name = ( - "smoke_projects_" - + p.relative_to(PROJECTS_ROOT).with_suffix("").as_posix().replace("/", "_").replace("-", "_") - ) - - try: - spec = importlib.util.spec_from_file_location(mod_name, p) - if spec is None or spec.loader is None: - raise RuntimeError(f"Cannot create import spec for {p}") - mod = importlib.util.module_from_spec(spec) - sys.modules[mod_name] = mod - spec.loader.exec_module(mod) - except Exception as e: - failures.append((str(p.relative_to(PROJECTS_ROOT)), repr(e))) +def test_import(): + import researchcodes - if failures: - msg = "\n".join([f"- {relpath}: {err}" for relpath, err in failures]) - raise AssertionError(f"Import smoke failed for some .py files under Projects:\n{msg}\n") + assert hasattr(researchcodes, "__version__")