This repository contains a Python code base with best practices designed to support your MLOps initiatives.
The package leverages several tools and tips to make your MLOps experience as flexible, robust, productive as possible.
You can use this package as part of your MLOps toolkit or platform (e.g., Model Registry, Experiment Tracking, Realtime Inference, ...).
Related Resources:
- LLMOps Coding Package (Example): Example with best practices and tools to support your LLMOps projects.
- MLOps Coding Course (Learning): Learn how to create, develop, and maintain a state-of-the-art MLOps code base.
- Cookiecutter MLOps Package (Template): Start building and deploying Python packages and Docker images for MLOps tasks.
- Agent Skills (Resource): Enhance your AI Agents with standardized skills for MLOps and coding.
- MLOps Python Package
- Table of Contents
- Install
- Usage
- Tools
- Tips
- Resources
This section details the requirements, actions, and next steps to kickstart your MLOps project.
- Python>=3.14: to benefit from the latest features and performance improvements
- uv>=0.13: to initialize the project virtual environment and its dependencies
- mise: to run the canonical project tasks (
install,format,check,test,build, andall) shared by git hooks and CI
- Clone this GitHub repository on your computer
# with ssh (recommended)
$ git clone git@github.com:fmind/mlops-python-package
# with https
$ git clone https://github.com/fmind/mlops-python-packagecd mlops-python-package/
mise trust && mise install
mise run install- Adapt the code base to your desire
Going from there, there are dozens of ways to integrate this package to your MLOps platform.
For instance, you can use Databricks or AWS as your compute platform and model registry.
It's up to you to adapt the package code to the solution you target. Good luck champ!
The bundled data comes from the UCI Bike Sharing dataset (data/hour.csv; attribution is in data/Readme.txt). The target cnt is the number of rentals per hour. casual and registered add up to the target and remain only in the raw CSV for analysis. The processing notebook removes them from every input Parquet file, so training and serving share a feature-only InputsSchema. Existing registered models keep their original signatures: retrain and register a new version before serving count-free inputs. Keep chronological splits and use only the training period for tuning and diagnostic curves.
The PipelineJob in confs/pipeline.yaml passes the full tuned model configuration and selected parameters into training, records the tuning run ID on the training run, and evaluates the exact registered candidate version on heldout data. It changes the Champion alias only after the thresholds pass. Inference and explanations then use that same version, even if another run registers a newer candidate.
Targets and predictions remain unsigned counts in the domain schemas. Evaluation converts them to floating point at the metric boundary so negative residuals cannot wrap into large positive errors.
This section explains how to configure the project code and execute it on your system.
You can add or edit config files in the confs/ folder to change the program behavior.
# confs/training.yaml
job:
KIND: TrainingJob
inputs:
KIND: ParquetReader
path: data/inputs_train.parquet
targets:
KIND: ParquetReader
path: data/targets_train.parquetThis config file instructs the program to start a TrainingJob with 2 parameters:
inputs: dataset that contains the model inputstargets: dataset that contains the model target
You can find all the parameters of your program in the src/bikes/jobs/*.py files.
You can also print the full schema supported by this package using uv run bikes --schema.
MLflow URI precedence is explicit YAML configuration, then MLFLOW_TRACKING_URI / MLFLOW_REGISTRY_URI, then local SQLite. Without a registry override, the registry follows the effective tracking URI. Copy .env.example to .env when using mise tasks. For Docker Compose, use mise run docker:compose: it initializes the SQLite file before mounting it; direct Compose startup requires mise run mlflow:db first.
Run notebooks with notebooks/ as the working directory and the project's .venv as the kernel. Run training before indicators.ipynb, and the explanations job before explain.ipynb. The indicators notebook follows the MLflow URI environment variables and defaults to the project's SQLite database.
The project code can be executed with uv during your development:
uv run bikes confs/pipeline.yaml
# Standalone stages remain available. Evaluate the recorded candidate first.
uv run bikes confs/training.yaml
uv run bikes confs/evaluations.yaml --extras '{"job": {"alias_or_version": 1}}'
MODEL_VERSION=1 uv run bikes confs/promotion.yamlIn production, you can build, ship, and run the project as a Python package:
uv build
uv publish # optional
python -m pip install dist/bikes-*.whl
bikes confs/inference.yamlYou can also use this package as a library. This example expects the bundled Parquet inputs (regenerate them with notebooks/processing.ipynb when needed):
from bikes import jobs
from bikes.io import datasets
job = jobs.TrainingJob(
inputs=datasets.ParquetReader(path="data/inputs_train.parquet"),
targets=datasets.ParquetReader(path="data/targets_train.parquet"),
)
with job as runner:
runner.run()Additional tips:
- You can pass extra configs from the command line using the
--extrasflag- Use it to pass runtime values (e.g., a result from previous job executions)
- You can pass several config files in the command-line to merge them from left to right
- You can define common configurations shared between jobs (e.g., model params)
- The right job task will be selected automatically thanks to Pydantic Discriminated Unions
- This is a great way to run any job supported by the application (training, tuning, ...)
This project includes several automation tasks to easily repeat common actions.
AI agents: read
AGENTS.mdfor the full stack, task vocabulary, and conventions before contributing.
You can run the tasks from the command-line or VS Code extension.
# execute the project DAG
$ mise run project
# build a code archive
$ mise run build
# list other actions
$ mise tasksUse mise tasks for the current list. The task definitions live in mise.toml.
This package supports three GitHub Workflows in .github/workflows:
ci.yml: runmise run all(format, check, test, build) and a clean-tree check on each push tomainand Pull Requestcd.yml: run the complete release gate, publish API docs to GitHub Pages, and scan a candidate Docker image before assigning the release andlatesttags to its immutable digest.security.yml: scan the full Git history and checkout withgitleaksandtrivyevery week
You can use and extend these workflows to automate repetitive package management tasks.
This sections motivates the use of developer tools to improve your coding experience.
Pre-defined actions to automate your project development.
AI Assistant: Gemini Code Assist
- Motivations:
- Increase your coding productivity
- Get code suggestions and completions
- Reduce the time spent on reviewing code
- Limitations:
- Can generate wrong code, reviews, or summaries
Commits: git-cliff
- Motivations:
- Standardize commits with Conventional Commits
- Generate a standard changelog from the git history
- Integrate well with SemVer and PEP 440
- Limitations:
- Requires disciplined commit messages
- Alternatives:
- Do It Yourself (DIY)
Dependabot: Dependabot
- Motivations:
- Avoid security issues
- Avoid breaking changes
- Update your dependencies
- Limitations:
- Can break your code
- Alternatives:
- Do It Yourself (DIY)
Git Hooks: Lefthook
- Motivations:
- Check your code locally before a commit
- Avoid wasting resources on your CI/CD
- Thin hooks that delegate to
mise runtasks
- Limitations:
- Add overhead before your commit
- Alternatives:
- Git Hooks: native but less convenient to use
Tasks: Mise
- Motivations:
- Automate project workflows
- One task vocabulary shared by git hooks and CI
- Also manages the project toolchain (Python, uv, dprint, gitleaks, trivy, hadolint, actionlint, zizmor, git-cliff)
- Limitations:
- Not familiar to most developers
- Alternatives:
Execution of automated workflows on code push and releases.
Runner: GitHub Actions
- Motivations:
- Native on GitHub
- Simple workflow syntax
- Lots of configs if needed
- Runs one command,
mise run all, so CI and local hooks cannot disagree
- Limitations:
- SaaS Service
- Alternatives:
- GitLab: can be installed on-premise
Workflow Linting: actionlint + zizmor
- Motivations:
- Workflows are code:
actionlintchecks their schema, expressions, and embedded shell zizmoraudits them for security (template injection, credential persistence, cache poisoning)- Both run offline inside
mise run check:actions, so a broken workflow fails before it is pushed
- Workflows are code:
- Limitations:
- Actions are pinned to full commit SHAs; version comments make updates reviewable under the default
zizmorpolicy
- Actions are pinned to full commit SHAs; version comments make updates reviewable under the default
- Alternatives:
- Discovering the mistake from a red run on
main
- Discovering the mistake from a red run on
Integrations with the Command-Line Interface (CLI) of your system.
Parser: Argparse
- Motivations:
- Provide CLI arguments
- Included in Python runtime
- Sufficient for providing configs
- Limitations:
- More verbose for advanced parsing
- Alternatives:
Logging: Loguru
- Motivations:
- Show progress to the user
- Work fine out of the box
- Saner logging syntax
- Limitations:
- Doesn't let you deviate from the base usage
- Alternatives:
- Logging: available by default, but feel dated
Edition, validation, and versioning of your project source code.
Coverage: Coverage
- Motivations:
- Report code covered by tests
- Identify code path to test
- Show maturity to users
- Measured through Pytest Cov, with a 100% gate (
--cov-fail-under=100)
- Limitations:
- None
- Alternatives:
- None
Editor: VS Code
- Motivations:
- Open source
- Free, simple, open source
- Great plugins for Python development
- Limitations:
- Require some configuration for Python
- Alternatives:
Formatting: Ruff
- Motivations:
- Super fast compared to others
- Don't waste time arranging your code
- Make your code more readable/maintainable
- Limitations:
- Still in version 0.x, but more and more adopted
- Alternatives:
Quality: Ruff
- Motivations:
- Improve your code quality
- Super fast compared to others
- Great integration with VS Code
- Limitations:
- None
- Alternatives:
Security: Ruff
- Motivations:
- Limitations:
- None
- Alternatives:
- Bandit: the standalone tool the
Srules are based on
- Bandit: the standalone tool the
Testing: Pytest
- Motivations:
- Write tests or pay the price
- Super easy to write new test cases
- Tons of good plugins (xdist, sugar, cov, ...)
- Limitations:
- Doesn't support parallel execution out of the box
- Alternatives:
- Unittest: more verbose, less fun
Typing: ty
- Motivations:
- Static typing is cool!
- Extremely fast, written in Rust by Astral
- Same maintainers and ecosystem as Ruff and uv
- Limitations:
- Pre-1.0, still stabilizing its type-checking rules
- Alternatives:
Versioning: Git
- Motivations:
- If you don't version your code, you are a fool
- Most popular source code manager (what else?)
- Provide hooks to perform automation on some events
- Limitations:
- Git can be hard: https://xkcd.com/1597/
- Alternatives:
- Mercurial: loved it back then, but git is the only real option
Manage the configs files of your project to change executions.
Format: YAML
- Motivations:
- Change execution without changing code
- Readable syntax, support comments
- Allow to use OmegaConf <3
- Limitations:
- Not supported out of the box by Python
- Alternatives:
Parser: OmegaConf
- Motivations:
- Parse and merge YAML files
- Powerful, doesn't get in your way
- Achieve a lot with few lines of code
- Limitations:
- Do not support remote files (e.g., s3, gcs, ...)
- You can combine it with cloudpathlib
- Do not support remote files (e.g., s3, gcs, ...)
- Alternatives:
Validator: Pydantic
- Motivations:
- Validate your config before execution
- Pydantic should be builtin (period)
- Super charge your Python class
- Limitations:
- None
- Alternatives:
Formatter: dprint
- Motivations:
- Format JSON, Markdown, TOML, and YAML files
- Fast, pluggable formatter written in Rust
- Keep configs and docs consistent alongside Ruff for Python
- Limitations:
- Separate tool from the Python formatter (Ruff)
- Alternatives:
- Prettier: broader language support, but slower and Node-based
Define the datasets to provide data inputs and outputs.
Container: Pandas
- Motivations:
- Load data files in memory
- Lingua franca for Python
- Most popular options
- Limitations:
- Lot of gotchas
- Alternatives:
Format: Parquet
- Motivations:
- Store your data on disk
- Column-oriented (good for analysis)
- Much more efficient and saner than text based
- Limitations:
- None
- Alternatives:
Schema: Pandera
- Motivations:
- Typing for dataframe
- Communicate data fields
- Support pandas and others
- Limitations:
- None
- Alternatives:
- Great Expectations: powerful, but much more difficult to integrate
Generate and share the project documentations.
API: pdoc
- Motivations:
- Share docs with others
- Simple tool, only does API docs
- Get the job done, get out of your way
- Limitations:
- Only support API docs (i.e., no custom docs)
- Alternatives:
Format: Google
- Motivations:
- Common style for docstrings
- Most writeable out of alternatives
- I often write a single line for simplicity
- Limitations:
- None
- Alternatives:
Hosting: GitHub Pages
- Motivations:
- Easy to setup
- Free and simple
- Integrated with GitHub
- Limitations:
- Only support static content
- Alternatives:
- ReadTheDocs: provide more features
Toolkit to handle machine learning models.
Evaluation: Scikit-Learn Metrics
- Motivations:
- Bring common metrics
- Avoid reinventing the wheel
- Avoid implementation mistakes
- Limitations:
- Limited set of metric to be chosen
- Alternatives:
- Implement your own: for custom metrics
Format: Mlflow Model
- Motivations:
- Standard ML format
- Store model dependencies
- Strong community ecosystem
- No pickle: the PyFunc adapter is logged as code (models from code) and the fitted pipeline with skops, which only reloads trusted types
- Limitations:
- None
- Alternatives:
- Pickle: work out of the box, but less suited for big array
- ONNX: great for deep learning, no guaranteed compatibility for the rest
Registry: Mlflow Registry
- Motivations:
- Save and load models
- Separate production from consumption
- Popular, open source, work on local system
- Limitations:
- None
- Alternatives:
- Comet: SaaS solution
- Weights and Biases: SaaS solution
Tracking: Mlflow Tracking
- Motivations:
- Keep track of metrics and params
- Allow to compare model performances
- Popular, open source, work on local system
- Limitations:
- None
- Alternatives:
- Comet: SaaS solution
- Weights and Biases: SaaS solution
MLflow 3: This package uses SQLite (
sqlite:///mlflow.db) for tracking and registry metadata, with artifacts under./mlruns. The file-store backend is disabled by default in current MLflow releases. Browse local runs withmise run mlflow:serve. Moving to a shared service also requires artifact access, authentication, and database operations; changing the URI configures the client connection.
Define and build modern Python package.
Evolution: Changelog
- Motivations:
- Communicate changes to user
- Generated from Conventional Commits with git-cliff
- Grouped by change type, as configured in
cliff.toml
- Limitations:
- None
- Alternatives:
- None
Format: Wheel
- Motivations:
- Has several advantages
- Create source code archive
- Most modern Python format
- Limitations:
- Doesn't ship with C/C++ dependencies (e.g., CUDA)
- i.e., use Docker containers for this case
- Doesn't ship with C/C++ dependencies (e.g., CUDA)
- Alternatives:
Manager: uv
- Motivations:
- Define and build Python package (with the
uv_buildbackend) - Fast and compliant package manager
- Pack every metadata in a single static file
- Define and build Python package (with the
- Limitations:
- Cannot add dependencies beyond Python (e.g., CUDA)
- i.e., use Docker container for this use case
- Cannot add dependencies beyond Python (e.g., CUDA)
- Alternatives:
- Setuptools: dynamic file is slower and more risky
- Poetry: previous solution of this package
- Pdm, Hatch, PipEnv: https://xkcd.com/1987/
Runtime: Docker
The runtime image contains the installed package and locked dependencies. It omits pip and its bootstrap wheels; rebuild the image when dependencies change.
- Motivations:
- Create isolated runtime
- Container is the de facto standard
- Package C/C++ dependencies with your project
- Limitations:
- Some company might block Docker Desktop, you should use alternatives
- Alternatives:
- Conda: slow and heavy resolver
Image Linting: hadolint
- Motivations:
- Catches Dockerfile mistakes (unpinned bases, root users, unresolvable uids) before a build
- Runs offline in under a second inside
mise run check:dockerfile
- Limitations:
- Lints the recipe;
trivyscans the checkout and the release workflow scans the built image before promoting its digest
- Lints the recipe;
- Alternatives:
- Reading the Dockerfile best practices page every time
Select your programming environment.
Language: Python
- Motivations:
- Great language for AI/ML projects
- Robust with additional tools
- Hundreds of great libs
- Limitations:
- Slow without C bindings
- Alternatives:
Version: Uv
- Motivations:
- Switch between Python version
- Allow to select the best version
- Support global and local dispatch
- Pinned in
.python-version(read by uv) and in themise.tomltoolchain
- Limitations:
- Keep both pins in sync when you upgrade Python
- Alternatives:
- Manual installation: time consuming
- PyEnv: shell-based, require more setup
Reproducibility: Mlflow Project
- Motivations:
- Share common project formats.
- Ensure the project can be reused.
- Avoid randomness in project execution.
- Limitations:
- Mlflow Project is best suited for small projects.
- Alternatives:
- DVC: both data and models.
- Metaflow: focus on machine learning.
- Apache Airflow: for large scale projects.
Run an MLflow Project job with
mise run project:run <job>(e.g.mise run project:run training); it passes--env-manager=localto reuse the uv-managed environment instead of creating a new one.
Monitoring : Mlflow Evaluate
- Motivations:
- Compute the model metrics.
- Validate model with thresholds.
- Perform post-training evaluations.
- Limitations:
- Mlflow Evaluate is less feature-rich as alternatives.
- Alternatives:
Alerting: Plyer
- Motivations:
- Simple solution.
- Send notifications on system.
- Cross-system: Mac, Linux, Windows.
- Limitations:
- Should not be used for large scale projects.
- Alternatives:
Lineage: Mlflow Dataset
- Motivations:
- Store information in Mlflow.
- Track metadata about run datasets.
- Keep URI of the dataset source (e.g., website).
- Limitations:
- Not as feature-rich as alternative solutions.
- Alternatives:
- Databricks Lineage: limited to Databricks.
- OpenLineage and Marquez: open-source and flexible.
Explainability: SHAP
- Motivations:
- Most popular toolkit.
- Support various models (linear, model, ...).
- Integration with Mlflow through the SHAP module.
- Limitations:
- Super slow on large dataset.
- Mlflow SHAP module is not mature enough.
- Alternatives:
- LIME: not maintained anymore.
Infrastructure: Mlflow System Metrics
- Motivations:
- Track infrastructure information (RAM, CPU, ...).
- Integrated with Mlflow tracking.
- Provide hardware insights.
- Limitations:
- Not as mature as alternative solutions.
- Alternatives:
- Datadog: popular and mature solution.
This sections gives some tips and tricks to enrich the develop experience.
You should decouple the pointer to your data from how to access it.
In your code, you can refer to your dataset with a tag (e.g., inputs, targets).
This tag can then be associated to a reader/writer implementation in a configuration file:
inputs:
KIND: ParquetReader
path: data/inputs_train.parquet
targets:
KIND: ParquetReader
path: data/targets_train.parquetIn this package, the implementation are described in src/bikes/io/datasets.py and selected by KIND.
You should select the best hyperparameters for your model using optimization search.
The simplest projects can use a sklearn.model_selection.GridSearchCV to scan the whole search space.
This package provides a simple interface to this hyperparameter search facility in src/bikes/utils/searchers.py.
For more complex project, we recommend to use more complex strategy (e.g., Bayesian) and software package (e.g., Optuna).
You should properly split your dataset into a training, validation, and testing sets.
- Training: used for fitting the model parameters
- Validation: used to find the best hyperparameters
- Testing: used to evaluate the final model performance
The sets should be exclusive, and the testing set should never be used as training inputs!
This package provides a simple deterministic strategy implemented in src/bikes/utils/splitters.py.
You should use Directed-Acyclic Graph (DAG) to connect the steps of your ML pipeline.
A DAG can express the dependencies between steps while keeping the individual step independent.
The project task in mise.toml runs PipelineJob from confs/pipeline.yaml: tuning → training → holdout evaluation → promotion → inference → explanations. Stage failures propagate; a failed evaluation preserves the existing champion. Selected parameters are saved as selected_params.json in the tuning run. Standalone promotion requires a positive explicit version; it never selects the latest version implicitly.
In production, we recommend to use a scalable system such as Airflow, Dagster, Prefect, Metaflow, or ZenML.
You should provide a global context for the execution of your program.
There are several approaches such as Singleton, Global Variable, or Component.
This package takes inspiration from Clojure mount. It provides an implementation in src/bikes/io/services.py.
You should separate the program implementation from the program configuration.
Exposing configurations to users allow them to influence the execution behavior without code changes.
This package seeks to expose as much parameter as possible to the users in configurations stored in the confs/ folder.
You should implement the SOLID principles to make your code as flexible as possible.
- Single responsibility principle: Class has one job to do. Each change in requirements can be done by changing exactly one class.
- Open/closed principle: Class is happy (open) to be used by others. Class is not happy (closed) to be changed by others.
- Liskov substitution principle: Class can be replaced by any of its children. Children classes inherit parent's behaviours.
- Interface segregation principle: When classes promise each other something, they should separate these promises (interfaces) into many small promises, so it's easier to understand.
- Dependency inversion principle: When classes talk to each other in a very specific way, they both depend on each other to never change. Instead classes should use promises (interfaces, parents), so classes can change as long as they keep the promise.
In practice, this mean you can implement software contracts with interface and swap the implementation.
For instance, you can implement several jobs in src/bikes/jobs/*.py and swap them in your configuration.
To learn more about the mechanism select for this package, you can check the documentation for Pydantic Tagged Unions.
You should separate the code interacting with the external world from the rest.
The external is messy and full of risks: missing files, permission issue, out of disk ...
To isolate these risks, you can put all the related code in an io package and use interfaces.
You should use Python context manager to control and enhance an execution.
Python provides contexts that can be used to extend a code block. For instance:
# in src/bikes/scripts.py
with setting.job as runner: # validated configuration
runner.run() # run in contextThis pattern has the same benefit as Monad, a powerful programming pattern.
The package uses src/bikes/jobs/*.py to handle exception and services.
You should create Python package to create both library and application for others.
Using Python package for your AI/ML project has the following benefits:
- Build code archive (i.e., wheel) that be uploaded to Pypi.org
- Install Python package as a library (e.g., like pandas)
- Expose script entry points to run a CLI or a GUI
To build a Python package with uv, you simply have to type in a terminal:
# for all uv project
uv build
# for this project only
mise run buildYou should type your Python code to make it more robust and explicit for your user.
Python provides the typing module for adding type hints and ty to checking them.
# in src/bikes/core/models.py
@abc.abstractmethod
def fit(self, inputs: schemas.Inputs, targets: schemas.Targets) -> T.Self:
"""Fit the model on the given inputs and target."""
@abc.abstractmethod
def predict(self, inputs: schemas.Inputs) -> schemas.Outputs:
"""Generate an output with the model for the given inputs."""This code snippet clearly state the inputs and outputs of the method, both for the developer and the type checker.
The package aims to type every functions and classes to facilitate the developer experience and fix mistakes before execution.
You should type your configuration to avoid exceptions during the program execution.
Pydantic allows to define classes that can validate your configs during the program startup.
# in src/bikes/utils/splitters.py
class TrainTestSplitter(Splitter):
shuffle: bool = False # required (time sensitive)
test_size: int | float = 24 * 30 * 2 # 2 months
random_state: int = 42This code snippet allows to communicate the values expected and avoid error that could be avoided.
The package combines both OmegaConf and Pydantic to parse YAML files and validate them as soon as possible.
You should type your dataframe to communicate and validate their fields.
Pandera supports dataframe typing for Pandas and other library like PySpark:
Selected fields from the complete schemas:
class InputsSchema(Schema):
instant: papd.Index[papd.UInt32] = pa.Field(ge=0, unique=True)
hr: papd.Series[papd.UInt8] = pa.Field(ge=0, le=23)
temp: papd.Series[papd.Float16] = pa.Field(ge=0, le=1)The shared schema parsers reject fractional or overflowing integer values and invalid booleans before coercion. This excerpt needs the module's imports and base schema.
This code snippet defines the fields of the dataframe and some of its constraint.
The package encourages to type every dataframe used in src/bikes/core/schemas.py.
You should use the Objected Oriented programming to benefit from polymorphism.
Polymorphism combined with SOLID Principles allows to easily swap your code components.
class Reader(abc.ABC, pdt.BaseModel):
@abc.abstractmethod
def read(self) -> pd.DataFrame:
"""Read a dataframe from a dataset."""This code snippet uses the abc module to define code interfaces for a dataset with a read/write method.
The package defines class interface whenever possible to provide intuitive and replaceable parts for your AI/ML project.
You should use semantic versioning to communicate the level of compatibility of your releases.
Semantic Versioning (SemVer) provides a simple schema to communicate code changes. For package X.Y.Z:
- Major (X): major release with breaking changed (i.e., imply actions from the benefit)
- Minor (Y): minor release with new features (i.e., provide new capabilities)
- Patch (Z): patch release to fix bugs (i.e., correct wrong behavior)
Uv and this package leverage Semantic Versioning to let developers control the speed of adoption for new releases.
You can run your tests in parallel to speed up the validation of your code base.
Pytest can be extended with the pytest-xdist plugin for this purpose.
This package runs it with mise run test:parallel (no coverage); the test gate stays serial because pytest-cov and pytest-xdist deadlock on this suite.
You should define reusable objects and actions for your tests with fixtures.
Fixture can prepare objects for your test cases, such as dataframes, models, files.
This package defines fixtures in tests/conftest.py to improve your testing experience.
You can use VS Code workspace to define configurations for your project.
Code Workspace can enable features (e.g. formatting) and set the default interpreter.
{
"settings": {
"editor.formatOnSave": true,
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python"
}
}This package defines a workspace file that you can load from mlops-python-package.code-workspace.
- Editor assistance can reduce repetitive work; measure its value on your own tasks.
GitHub Copilot has been a huge productivity boost thanks to its smart completion.
You should become familiar with the solution in less than a single coding session.
You can use VIM keybindings to more efficiently navigate and modify your code.
- Editor assistance can reduce repetitive work; measure its value on your own tasks.
Compared to GitHub Copilot, VIM can take much more time to master. Measure whether the navigation gains repay the learning time for your work.
This section provides resources for building packages for Python and AI/ML/MLOps.
- https://github.com/krzjoa/awesome-python-data-science#readme
- https://github.com/ml-tooling/best-of-ml-python
- https://github.com/ml-tooling/best-of-python
- https://github.com/ml-tooling/best-of-python-dev
- https://github.com/vinta/awesome-python
mise run clean removes caches and build outputs while preserving MLflow databases, run artifacts, and predictions. Back up experiment state before any deliberate manual reset.
Use mise run mlflow:serve to browse experiments created by local jobs: their artifact URIs contain host paths. Compose can serve metadata from the same SQLite file, but those host paths are not automatically available inside the container. For new HTTP-first experiments, configure clients to reach the server and choose server-accessible artifact storage.
The release image gate checks vulnerabilities, secrets and misconfigurations. It records license classifications separately: the Debian base includes reciprocal licenses that require an inventory, rather than a blanket license prohibition.
