djxi is a Django library that eliminates the HTMX scattering problem: it bundles URL patterns, view logic, and HTML template sections for a feature into a single DXEndpointBattery class, keeping all related code in one place.
python-catalin
Python tutorials with source code, examples, guides, and tips and tricks for Windows and Linux development.
Tuesday, July 28, 2026
Monday, July 27, 2026
Python : KivyMD on pydroid android I.D.E.
This Python script is a cross-platform mobile application built with KivyMD, a Material Design framework extension for Python's Kivy library. It combines a declarative user interface layout defined via Kivy Language (KV string) with Python event handling to create an interactive task management dashboard tailored for Android environments like Pydroid 3.



Saturday, July 25, 2026
Python : simple educational planisphere
This Python script functions as an educational planisphere designed for telescope observation. Instead of taking a live snapshot of the current sky (where most events would be hidden below the horizon during the day or off-season), it calculates and displays the expected positions of major astronomical events through late 2026—such as meteor shower radiants and NEO asteroid flybys—relative to Fălticeni, Romania (47.46^\circ\text{ N}, 26.30^\circ\text{ E}).
Oriented with North at the top, the code converts equatorial coordinates (Right Ascension and Declination) into local horizon coordinates (Azimuth and Altitude). It then projects these targets onto a 2D polar plot, indicating the cardinal direction (N, E, S, W) to point your telescope and labeling each event with its peak date and estimated rise time, before automatically exporting the chart as a high-resolution PNG image to your device's Download folder.

Let's see source code:
import os
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime, timezone
def draw_and_save_sky_map():
# Coordonate Fălticeni, România
LAT = 47.46
LON = 26.30
# Calendarul evenimentelor până la sfârșitul anului 2026
events = [
{
'name': 'Perseide (Vârf)',
'date_str': '12 Aug',
'rise_time': '22:00',
'ra': 3.07, 'dec': 58.0,
'color': '#00ffcc', 'marker': '*'
},
{
'name': 'Orionide (Vârf)',
'date_str': '21 Oct',
'rise_time': '23:30',
'ra': 6.34, 'dec': 16.0,
'color': '#ff9900', 'marker': '*'
},
{
'name': 'Leonide (Vârf)',
'date_str': '17 Noi',
'rise_time': '00:15',
'ra': 10.2, 'dec': 22.0,
'color': '#ff3366', 'marker': '*'
},
{
'name': 'Geminide (Vârf)',
'date_str': '13 Dec',
'rise_time': '20:30',
'ra': 7.46, 'dec': 33.0,
'color': '#ffff00', 'marker': '*'
},
{
'name': 'Asteroid NEO 2026',
'date_str': 'Toamnă 2026',
'rise_time': '21:00',
'ra': 14.5, 'dec': 10.0,
'color': '#ff00ff', 'marker': 's'
}
]
# Calcul Timp Sidereal Local (LST)
now_utc = datetime.now(timezone.utc)
j2000_epoch = datetime(2000, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
days_since_j2000 = (now_utc - j2000_epoch).total_seconds() / 86400.0
lst = (18.697374558 + 24.06570982441908 * days_since_j2000 + LON / 15.0) % 24
# Creare grafic Matplotlib (Polar)
fig = plt.figure(figsize=(8, 8), facecolor='#0b0d17')
ax = fig.add_subplot(111, polar=True, facecolor='#05070f')
# Orientare fixă: NORDUL SUS (0°)
ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
ax.set_rlim(0, 90)
ax.set_yticklabels([])
lat_rad = np.radians(LAT)
for item in events:
ha = (lst - item['ra']) * 15.0
ha_rad = np.radians(ha)
dec_rad = np.radians(item['dec'])
sin_alt = np.sin(dec_rad) * np.sin(lat_rad) + np.cos(dec_rad) * np.cos(lat_rad) * np.cos(ha_rad)
alt = np.degrees(np.arcsin(np.clip(sin_alt, -1.0, 1.0)))
if alt <= 5:
alt = 35.0
cos_az = (np.sin(dec_rad) - np.sin(lat_rad) * sin_alt) / (np.cos(lat_rad) * np.sin(np.arccos(sin_alt)))
az = np.degrees(np.arccos(np.clip(cos_az, -1.0, 1.0)))
if np.sin(ha_rad) > 0:
az = 360 - az
theta = np.radians(az)
r = 90 - alt
ax.plot(theta, r, marker=item['marker'], markersize=12, color=item['color'])
label_text = f"{item['name']}\n[{item['date_str']}]\nRăsare ~{item['rise_time']}"
ax.text(theta, r + 7, label_text, color=item['color'],
fontsize=7.5, ha='center', fontweight='bold')
cardinals = [('NORD (0°)', 0), ('EST (90°)', 90), ('SUD (180°)', 180), ('VEST (270°)', 270)]
for label, angle in cardinals:
ax.text(np.radians(angle), 102, label, color='white', fontsize=9, fontweight='bold', ha='center', va='center')
ax.set_title("HARTĂ EVENIMENTE ASTRONOMICE 2026 (Aug - Dec)\nLocație: Fălticeni | Orientare: Nordul Sus\n",
color='white', fontsize=9, pad=15)
ax.grid(color='#1a233a', linestyle=':', linewidth=0.8)
plt.tight_layout()
# --- SALVARE IMAGINE ÎN FOLDERUL DOWNLOAD ---
download_path = "/sdcard/Download"
# Alternativă în caz că calea /sdcard nu este mapată la fel pe unele versiuni de Android
if not os.path.exists(download_path):
download_path = os.path.expanduser("~/storage/downloads")
file_name = f"harta_astronomica_2026_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
full_path = os.path.join(download_path, file_name)
try:
plt.savefig(full_path, dpi=300, facecolor=fig.get_facecolor(), edgecolor='none')
print(f" Imaginea a fost salvată cu succes la:\n{full_path}")
except Exception as e:
print(f" Eroare la salvarea fișierului: {e}")
plt.show()
if __name__ == "__main__":
draw_and_save_sky_map()
Tuesday, July 21, 2026
Python 3.10.11 : testing default example with the pyui-0.1.0 python module.
A declarative, cross-platform GUI framework (ala SwiftUI) written in Python using SDL2. It provides tools and libraries that enable developers to create and manage user interfaces for desktop apps.
You need to install the pyui python module and the SDK with the pip tool.
python -m pip install pyui
Collecting pyui
Downloading pyui-0.1.0-py3-none-any.whl.metadata (893 bytes)
Collecting PySDL2>=0.9.7 (from pyui)
Downloading PySDL2-0.9.17-py3-none-any.whl.metadata (3.8 kB)
Downloading pyui-0.1.0-py3-none-any.whl (6.0 MB)
---------------------------------------- 6.0/6.0 MB 6.0 MB/s 0:00:01
Downloading PySDL2-0.9.17-py3-none-any.whl (583 kB)
---------------------------------------- 583.1/583.1 kB 2.1 MB/s 0:00:00
Installing collected packages: PySDL2, pyui
Successfully installed PySDL2-0.9.17 pyui-0.1.0
python -m pip install pysdl2-dll
Collecting pysdl2-dll
Downloading pysdl2_dll-2.32.10-py2.py3-none-win_amd64.whl.metadata (4.7 kB)
Downloading pysdl2_dll-2.32.10-py2.py3-none-win_amd64.whl (4.1 MB)
---------------------------------------- 4.1/4.1 MB 4.1 MB/s 0:00:01
Installing collected packages: pysdl2-dll
Successfully installed pysdl2-dll-2.32.10Let's see the default example with one grid.

Let's see the source code:
import pyui
class ItemGridView(pyui.View):
def content(self):
yield pyui.ScrollView(axis=self.axis)(
pyui.Grid(num=self.num, size=self.size, axis=self.axis, flex=self.flex)(
pyui.ForEach(
range(self.item_count),
lambda num: (
pyui.Rectangle()(pyui.Text(num + 1).color(255, 255, 255))
.background(120, 120, 120)
.radius(5)
.animate()
),
)
)
)
class GridTest(pyui.View):
axis = pyui.State(default=1)
item_count = pyui.State(int, default=50)
size = pyui.State(default=100)
num = pyui.State(default=4)
size_or_num = pyui.State(default=0)
flex = pyui.State(default=False)
def content(self):
if self.size_or_num.value == 0:
size = None
num = self.num.value
else:
size = self.size.value
num = None
yield pyui.HStack(alignment=pyui.Alignment.LEADING)(
pyui.VStack(alignment=pyui.Alignment.LEADING)(
pyui.Text("Axis"),
pyui.SegmentedButton(self.axis)(
pyui.Text(pyui.Axis.HORIZONTAL.name),
pyui.Text(pyui.Axis.VERTICAL.name),
),
pyui.HStack(
pyui.Text("Number of items"),
pyui.Spacer(),
pyui.Text(self.item_count.value)
.color(128, 128, 128)
.priority("high"),
),
pyui.Slider(self.item_count, maximum=200),
pyui.Text("Fill rows/columns by"),
pyui.SegmentedButton(self.size_or_num)(
pyui.Text("Number"),
pyui.Text("Size"),
),
pyui.HStack(
pyui.Text("Items per row/column"),
pyui.Spacer(),
pyui.Text(self.num.value).color(128, 128, 128).priority("high"),
),
pyui.Slider(self.num, minimum=1, maximum=10).disable(
self.size_or_num.value == 1
),
pyui.HStack(
pyui.Text("Item size"),
pyui.Spacer(),
pyui.Text(self.size.value).color(128, 128, 128).priority("high"),
),
pyui.Slider(self.size, minimum=50, maximum=200).disable(
self.size_or_num.value == 0
),
pyui.Toggle(self.flex, label="Adjust size to fit").disable(
self.size_or_num.value == 0
),
)
.padding(10)
.size(width=300),
ItemGridView(
item_count=self.item_count.value,
size=size,
num=num,
axis=self.axis.value,
flex=self.flex.value,
),
)
if __name__ == "__main__":
app = pyui.Application("io.temp.GridTest")
app.window("Grid Tester", GridTest())
app.run()
Posted by
Cătălin George Feștilă
Labels:
2026,
module,
modules,
packages,
programming,
python,
python modules,
python packages,
python3,
pyui,
tutorial,
tutorials
Saturday, July 18, 2026
Python Qt : testing Browserless.io feature ...
The provided PyQt6 application is a desktop tool that allows a user to input any website address and instantly obtain a full‑page screenshot of that site using the Browserless.io cloud rendering service; the program begins by creating a simple graphical interface composed of a text field for entering a URL, a button that triggers the screenshot process, and a display area where the resulting image is shown, all arranged vertically for clarity and ease of use. When the user presses the button, the application reads the typed URL, constructs a request to the Browserless screenshot API using the user’s authentication token, and sends a JSON payload instructing Browserless to load the page and capture it in full, not just the visible viewport. Browserless processes the request by launching a headless Chromium instance, navigating to the specified website, rendering the page completely—including dynamic content—and returning a PNG image representing the entire scrollable webpage. The application receives this binary PNG data through an HTTP response, converts it into a QPixmap object, and displays it directly inside the PyQt window, effectively turning the GUI into a live screenshot viewer. This design allows developers to visually inspect websites without embedding a browser engine locally, relying instead on Browserless’s remote infrastructure to handle rendering, JavaScript execution, and screenshot generation. The code is intentionally minimal, focusing on clarity and functionality: it uses only standard PyQt widgets, avoids unnecessary complexity, and demonstrates how to integrate cloud‑based browser automation into a Python desktop application. The result is a lightweight tool that can preview websites, capture full‑page screenshots, and serve as a foundation for more advanced features such as zooming, saving images, asynchronous loading, or automated website testing workflows.
This script was created with copilot artificial intelligence, and tested by me.
import sys
import requests
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QPushButton, QLabel
from PyQt6.QtGui import QPixmap
from PyQt6.QtCore import Qt
# PUNE TOKEN-UL TAU AICI, CA STRING
BROWSERLESS_TOKEN = "fill_TOKEN_official__website"
class BrowserlessViewer(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Browserless Web Screenshot Viewer")
self.resize(900, 700)
layout = QVBoxLayout(self)
self.url_edit = QLineEdit()
self.url_edit.setPlaceholderText("Introdu adresa web...")
layout.addWidget(self.url_edit)
self.btn = QPushButton("Generează Screenshot")
self.btn.clicked.connect(self.take_screenshot)
layout.addWidget(self.btn)
self.canvas = QLabel()
self.canvas.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.canvas)
def take_screenshot(self):
url = self.url_edit.text().strip()
if not url:
return
api_url = f"https://chrome.browserless.io/screenshot?token={BROWSERLESS_TOKEN}"
payload = {
"url": url,
"options": {
"fullPage": True
}
}
try:
r = requests.post(api_url, json=payload)
r.raise_for_status()
pixmap = QPixmap()
pixmap.loadFromData(r.content)
self.canvas.setPixmap(pixmap)
except Exception as e:
print("Eroare:", e)
if __name__ == "__main__":
app = QApplication(sys.argv)
viewer = BrowserlessViewer()
viewer.show()
sys.exit(app.exec())
Posted by
Cătălin George Feștilă
Labels:
2026,
artificial intelligence,
module,
modules,
packages,
programming,
PyQt6,
python,
python modules,
python packages,
python3,
request,
sys
Thursday, July 9, 2026
Wednesday, July 8, 2026
Python Qt : Script for Game Engine projects.
Simple python example script for Godot Engine projects.
This script allows you to see how big is the project, size of media files, size of scripting files, lines of source code and more:
import sys
import os
import pathlib
import datetime
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout,
QPushButton, QTextEdit, QProgressBar, QFileDialog, QMessageBox
)
from PyQt6.QtCore import Qt, QThread, pyqtSignal
def human_size(num_bytes: int) -> str:
units = ["B", "KB", "MB", "GB", "TB"]
size = float(num_bytes)
for unit in units:
if size < 1024.0:
return f"{size:.2f} {unit}"
size /= 1024.0
return f"{size:.2f} PB"
class FolderStatsWorker(QThread):
progress = pyqtSignal(int)
finished = pyqtSignal(str)
def __init__(self, folder_path: str):
super().__init__()
self.folder_path = folder_path
def run(self):
base = pathlib.Path(self.folder_path)
all_files = []
for root, dirs, files in os.walk(base):
for name in files:
all_files.append(pathlib.Path(root) / name)
total_files = len(all_files)
gd_files = []
media_files = []
total_lines_gd = 0
media_exts = {
".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp",
".ogg", ".wav", ".mp3", ".flac",
".mp4", ".mkv", ".avi", ".webm",
".md"
}
total_size = 0
gd_size = 0
media_size = 0
tree_lines = []
# Build tree view
for root, dirs, files in os.walk(base):
rel_root = pathlib.Path(root).relative_to(base)
indent_level = len(rel_root.parts)
indent = " " * indent_level
tree_lines.append(f"{indent}{rel_root if rel_root.parts else base.name}/")
for name in files:
tree_lines.append(f"{indent} {name}")
for idx, f in enumerate(all_files):
try:
stat = f.stat()
total_size += stat.st_size
ext = f.suffix.lower()
if ext == ".gd":
gd_files.append(f)
gd_size += stat.st_size
try:
txt = f.read_text(encoding="utf-8", errors="ignore")
if txt:
total_lines_gd += txt.count("\n") + 1
except Exception:
pass
if ext in media_exts:
media_files.append(f)
media_size += stat.st_size
except Exception:
pass
if total_files > 0:
self.progress.emit(int((idx + 1) * 100 / total_files))
report_lines = []
report_lines.append(f"Folder: {self.folder_path}")
report_lines.append("")
report_lines.append("=== Summary ===")
report_lines.append(f"Total files: {total_files}")
report_lines.append(f"Total .gd files: {len(gd_files)}")
report_lines.append(f"Total media files (incl. .md): {len(media_files)}")
report_lines.append(f"Total lines in .gd files: {total_lines_gd}")
report_lines.append("")
report_lines.append(f"Total size: {human_size(total_size)}")
report_lines.append(f"Total .gd size: {human_size(gd_size)}")
report_lines.append(f"Total media size: {human_size(media_size)}")
report_lines.append("")
report_lines.append("=== Tree ===")
report_lines.extend(tree_lines)
report_text = "\n".join(report_lines)
# Save report
now = datetime.datetime.now()
ts = now.strftime("%Y%m%d_%H%M%S")
folder_name = base.name
report_name = f"{folder_name}_{ts}.txt"
report_path = base / report_name
try:
report_path.write_text(report_text, encoding="utf-8")
except Exception:
pass
self.finished.emit(report_text)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Godot Project Stats")
self.setFixedSize(640, 480)
self.folder_path = None
self.worker = None
central = QWidget()
layout = QVBoxLayout(central)
self.btn_open = QPushButton("Open Folder")
self.btn_process = QPushButton("Process")
self.progress = QProgressBar()
self.progress.setRange(0, 100)
self.text = QTextEdit()
self.text.setReadOnly(True)
layout.addWidget(self.btn_open)
layout.addWidget(self.btn_process)
layout.addWidget(self.progress)
layout.addWidget(self.text)
self.setCentralWidget(central)
self.btn_open.clicked.connect(self.open_folder)
self.btn_process.clicked.connect(self.start_processing)
def open_folder(self):
folder = QFileDialog.getExistingDirectory(self, "Select Godot Project Folder")
if folder:
self.folder_path = folder
self.text.clear()
self.text.append(f"Selected folder:\n{folder}")
def start_processing(self):
if not self.folder_path:
QMessageBox.warning(self, "No folder", "Please select a folder first.")
return
if self.worker is not None and self.worker.isRunning():
QMessageBox.information(self, "Processing", "Already processing.")
return
self.progress.setValue(0)
self.text.append("\nProcessing...")
self.worker = FolderStatsWorker(self.folder_path)
self.worker.progress.connect(self.progress.setValue)
self.worker.finished.connect(self.on_finished)
self.worker.start()
def on_finished(self, report_text: str):
self.text.clear()
self.text.append(report_text)
self.progress.setValue(100)
def main():
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
Posted by
Cătălin George Feștilă
Labels:
2026,
artificial intelligence,
module,
modules,
packages,
programming,
PyQt6,
python,
python modules,
python packages,
python3
Tuesday, July 7, 2026
Python Qt : simple tool to convert any source code to html area development.
Today. I will show this simple idea to convert any source code for html tag code with PyQt6.
The source code of this tool is not very big.I don't share because can be simple or you can customize as you want starting with any artificial intelligence and is more easy for you.
I used this on my blogger area, let's see the result for a simple example source code Google Apps Script from Gemini AI to html source code with div , pre and code html tags included in the result:

Posted by
Cătălin George Feștilă
Labels:
2026,
artificial intelligence,
module,
modules,
packages,
programming,
PyQt6,
python,
python modules,
python packages,
python3
Python Qt : simple tool for audio dialogue in game development
Today, this script tool is a small desktop tool that lets you visually synchronize spoken audio with written dialogue. You load an audio file and the program generates a waveform so you can click or drag to select exact time ranges. At the same time, you select the matching text, and the tool creates timestamped dialogue segments.
Each segment includes a dialog ID, start time, end time, and the associated text. Segments appear in a list, can be played individually, deleted, or tested in a separate window where each dialog ID becomes a playback button. This makes it easy to verify timing and structure.
The entire project—audio path, full text, and all segments—can be saved or loaded as a JSON file. Older JSON formats containing only segments are automatically converted. The final JSON is ready for use in game engines like Godot or Unity for precise voice‑over playback.
I used artificial intelligence to fix some issues, this is the result:

Posted by
Cătălin George Feștilă
Labels:
2026,
artificial intelligence,
json,
module,
modules,
os,
packages,
pathlib,
plotly,
programming,
PyQt6,
python,
python modules,
python packages,
python3,
subppocess,
sys
Monday, July 6, 2026
Tools : TraceFlow artificial intelligence for python tasks.
This tool with artificial intelligence can help with python programming tasks.
TraceFlow captures crashes, traces every variable, and streams an AI-powered fix — all inside VSCode. No context switching. No guessing.
Perfect for trying out TraceFlow on personal projects.
- 5 analysis per day
- Crash detection & variable tracing
- AI root cause explanation
- One-click fix apply
- Call stack inspection
- Module & function level analysis
See the official website.
Posted by
Cătălin George Feștilă
Labels:
2026,
artificial intelligence,
python,
python3,
tool,
tools
Sunday, July 5, 2026
Python 3.10.11 : python library-skills for your artificial intelligence.
The Python package library-skills is a lightweight command‑line toolkit designed to help developers build, test, and validate modular AI “skills.” These skills are small, self‑contained units of logic that can be executed independently or integrated into larger AI agents. The package focuses on simplicity, portability, and clear structure, making it useful for developers who want to experiment with tool‑calling systems or create custom capabilities for AI workflows.
A skill typically consists of a JSON descriptor and a Python function. The JSON file defines the skill’s name, description, and input schema, while the Python file contains the actual execution logic. This separation ensures that skills remain easy to document, validate, and reuse across different projects. With library-skills, developers can quickly inspect a skill’s schema, run it with custom input, or verify that its output matches the expected structure.
The command‑line interface provided by the package allows users to list installed skills, execute them directly, and validate input files without writing additional code. This makes the development cycle faster and more predictable. Instead of manually wiring functions together, developers can rely on a consistent interface that handles loading, parsing, and execution.
One of the main advantages of library-skills is its role in AI agent development. Modern agents often rely on tool‑calling, where the AI selects and triggers external functions based on user intent. Skills created with this package can be easily integrated into such agents, providing clear schemas and predictable behavior. This helps ensure that AI systems remain reliable, debuggable, and easy to extend.
Overall, library-skills is a practical utility for anyone building structured AI tools. It encourages clean design, modularity, and transparency, making it a valuable addition to Python environments focused on AI experimentation and agent development.
Let's install this python package.
python -m pip install library-skills
Collecting library-skills
Downloading library_skills-0.0.19-py3-none-any.whl.metadata (5.4 kB)
...
Successfully installed library-skills-0.0.19 rich-toolkit-0.20.1 tomli-2.4.1Let's make the first run:
library-skills.exe
context
Project root c:\lucru\PythonProjects
Target Python environment not found
Warning: No target Python environment with site-packages or node_modules was found. Run from a
project root after installing dependencies, for example with 'uv sync' for Python or 'npm install' for
Node.js.
No installed or discovered skills found.
Copy the Library Skills tool skill into the project so agents know how to update, repair, and check
managed skills?
■ Copy Library Skills tool skill into the project so agents know how to update, repair, and check
managed skills? Copy Library Skills tool skill
Target Status Path
universal tool skill: missing .agents\skills\library-skills
Copied: library-skills (universal) -> .agents\skills\library-skillsNow you can create skills for your artificial intelligence:
Create a folder with two files:
my_skill/
skill.json
skill.py
First file named skill.json:
{
"name": "hello_skill",
"description": "Returnează un mesaj simplu",
"input_schema": {
"type": "object",
"properties": {
"name": {"type": "string"}
},
"required": ["name"]
}
}
The second file named skill.py
def run(input):
name = input["name"]
return {"message": f"Salut, {name}!"}
Run the skill into your folder:
library-skills run my_skill --input '{"name": "Catalin"}'See the schema:
library-skills schema my_skillValidate the input:
library-skills validate my_skill input.jsonList the skills:
library-skills listThis is all you need for a default basic skill with the library-skills.
Posted by
Cătălin George Feștilă
Labels:
2026,
artificial intelligence,
module,
modules,
packages,
programming,
python,
python modules,
python packages,
python3,
tutorial,
tutorials
Python 3.10.11 : CVSS (Common Vulnerability Scoring System) with cvss python package.
This Python package contains CVSS v2, v3 and v4 computation utilities and interactive calculator (for v2 and v3 only) compatible with Python 3. CVSS (Common Vulnerability Scoring System) is an standardized method for rating the severity of security issues on a scale from 0 (no impact) to 10 (critical).
See the pypi.org - cvss website.
Let's install the cvss python package.
python -m pip install cvss
Collecting cvss
Downloading cvss-3.6-py2.py3-none-any.whl.metadata (3.8 kB)
Downloading cvss-3.6-py2.py3-none-any.whl (31 kB)
Installing collected packages: cvss
WARNING: The script cvss_calculator.exe is installed in 'C:\python-3_10_11\Scripts' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed cvss-3.6How this works:
NVD Database (online)
|
| JSON feed
v
Python script ----> parses CVE + CVSS vector
|
| uses cvss library
v
Scores vulnerabilities (Base, Temporal, Environmental)
|
| inserts results
v
Your local database (SQL)
|
v
Dashboard / API / Alerts Simple code source example :
#!/usr/bin/env python3
# Demonstrates how to score a CVSS vector using the open-source "cvss" library.
# Validation and error handling included.
from cvss import CVSS3 # CVSS2, CVSS3, CVSS4 are available
import sys
def score_cvss_vector(vector: str):
"""
Validates and scores a CVSS3 vector string.
Returns scores and severities.
"""
if not isinstance(vector, str) or not vector.strip():
raise ValueError("Vector must be a non-empty string.")
try:
c = CVSS3(vector)
except Exception as e:
raise ValueError(f"Invalid CVSS3 vector: {e}")
return c.clean_vector(), c.scores(), c.severities()
def main():
if len(sys.argv) != 2:
print("Usage: python cvss_score.py '<CVSS3_VECTOR>'")
print("Example:")
print("python main.py 'CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H'")
sys.exit(1)
vector = sys.argv[1]
try:
clean_v, scores, severity = score_cvss_vector(vector)
print("Input vector:", vector)
print("Normalized vector:", clean_v)
print("Scores:", scores)
print("Severity:", severity)
except ValueError as e:
print("Error:", e)
sys.exit(1)
if __name__ == "__main__":
main()The result is this:
python main.py CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Input vector: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Normalized vector: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Scores: (9.8, 9.8, 9.8)
Severity: ('Critical', 'Critical', 'Critical')Another source code with examples:
#!/usr/bin/env python3
# Requires: pip install cvss
from cvss import CVSS3
# Example vulnerabilities (safe, educational)
vulns = [
{
"language": "Python",
"title": "Unsafe eval usage",
"description": "Code that executes user-provided input using eval().",
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N"
},
{
"language": "C#",
"title": "Insecure deserialization",
"description": "BinaryFormatter deserialization of untrusted data.",
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
},
{
"language": "Godot Engine",
"title": "Unvalidated file path access",
"description": "Loading files from paths provided by the user without validation.",
"cvss_vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N"
}
]
def analyze_vulnerabilities(vuln_list):
for v in vuln_list:
print("\n====================================")
print("Language:", v["language"])
print("Issue:", v["title"])
print("Description:", v["description"])
print("CVSS Vector:", v["cvss_vector"])
try:
cv = CVSS3(v["cvss_vector"])
base, temp, env = cv.scores()
sev_base, sev_temp, sev_env = cv.severities()
print("Base Score:", base, "-", sev_base)
print("Temporal Score:", temp, "-", sev_temp)
print("Environmental Score:", env, "-", sev_env)
except Exception as e:
print("Invalid CVSS vector:", e)
if __name__ == "__main__":
analyze_vulnerabilities(vulns)This is the result:
python main_002.py
====================================
Language: Python
Issue: Unsafe eval usage
Description: Code that executes user-provided input using eval().
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N
Base Score: 8.1 - High
Temporal Score: 8.1 - High
Environmental Score: 8.1 - High
====================================
Language: C#
Issue: Insecure deserialization
Description: BinaryFormatter deserialization of untrusted data.
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Base Score: 9.8 - Critical
Temporal Score: 9.8 - Critical
Environmental Score: 9.8 - Critical
====================================
Language: Godot Engine
Issue: Unvalidated file path access
Description: Loading files from paths provided by the user without validation.
CVSS Vector: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
Base Score: 4.4 - Medium
Temporal Score: 4.4 - Medium
Environmental Score: 4.4 - Medium
Posted by
Cătălin George Feștilă
Labels:
2026,
cvss,
module,
modules,
packages,
programming,
python,
python modules,
python packages,
python3,
sys,
tutorial,
tutorials
Subscribe to:
Posts (Atom)