Skip to content
Merged
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
19 changes: 2 additions & 17 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,8 @@ jobs:
toolchain: 1.88
components: clippy

- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}

- name: Cache cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}

- name: Cache cargo build --release
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo
uses: Swatinem/rust-cache@v2

- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D warnings
Expand Down
13 changes: 2 additions & 11 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,8 @@ jobs:
with:
toolchain: 1.88

- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}

- name: Cache cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo
uses: Swatinem/rust-cache@v2

- name: Build documentation
run: cargo doc --all-features --no-deps
Expand Down
19 changes: 2 additions & 17 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,8 @@ jobs:
with:
toolchain: ${{ matrix.rust }}

- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}

- name: Cache cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}

- name: Cache cargo build --release
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-${{ matrix.rust }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo
uses: Swatinem/rust-cache@v2

- name: Run tests
run: cargo test --all-features
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- **Logging Module** (Issue #34)
- Complete implementation of Python's `logging` standard library module
- Log level constants: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`, `NOTSET`
- Aliases: `WARN` (for `WARNING`), `FATAL` (for `CRITICAL`)
- Logging functions: `debug()`, `info()`, `warning()`, `error()`, `critical()`, `exception()`, `log()`
- Configuration: `basicConfig()`, `setLevel()`, `disable()`
- Logger management: `getLogger()`
- Handler support: `addHandler()`, `removeHandler()`
- Classes: `Logger`, `Handler`, `StreamHandler`, `FileHandler`, `Formatter`, `Filter`, `LogRecord`
- Test suite in `examples/test_logging.py`

- **JSON Module Runtime Implementation** (Issue #31)
- Implemented runtime support for `json.dumps()` - serialize Python objects to JSON strings
- Implemented runtime support for `json.loads()` - parse JSON strings to Python objects
Expand Down
3 changes: 2 additions & 1 deletion docs/modules.html
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,8 @@ <h1 class="title">Waspy Development Board</h1>
{ name: "datetime module (datetime, date, time, timedelta, timezone, tzinfo, now, today, fromtimestamp, fromisoformat, strftime, strptime, replace, timestamp, isoformat, weekday, isoweekday, MINYEAR, MAXYEAR)", status: "done", version: "0.8.0" },
{ name: "collections module (namedtuple, deque, Counter, OrderedDict, defaultdict, ChainMap, UserDict, UserList, UserString)", status: "done", version: "0.8.0" },
{ name: "itertools module (count, cycle, repeat, chain, compress, dropwhile, filterfalse, groupby, islice, starmap, takewhile, tee, zip_longest, product, permutations, combinations, combinations_with_replacement, accumulate, batched, pairwise)", status: "done", version: "0.8.0" },
{ name: "functools module (reduce, partial, partialmethod, wraps, update_wrapper, total_ordering, cmp_to_key, lru_cache, cache, cached_property, singledispatch, singledispatchmethod)", status: "done", version: "0.8.0" }
{ name: "functools module (reduce, partial, partialmethod, wraps, update_wrapper, total_ordering, cmp_to_key, lru_cache, cache, cached_property, singledispatch, singledispatchmethod)", status: "done", version: "0.8.0" },
{ name: "logging module (DEBUG, INFO, WARNING, ERROR, CRITICAL, NOTSET, debug, info, warning, error, critical, exception, log, basicConfig, getLogger, setLevel, disable, Logger, Handler, StreamHandler, FileHandler, Formatter, Filter, LogRecord)", status: "done", version: "unreleased" }
]
},
comprehensions: {
Expand Down
68 changes: 68 additions & 0 deletions examples/test_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import logging

def test_log_levels():
"""Test basic logging level constants."""
debug = logging.DEBUG
info = logging.INFO
warning = logging.WARNING
error = logging.ERROR
critical = logging.CRITICAL
notset = logging.NOTSET
return 0

def test_basic_logging():
"""Test basic logging functions."""
logging.debug("This is a debug message")
logging.info("This is an info message")
logging.warning("This is a warning message")
logging.error("This is an error message")
logging.critical("This is a critical message")
return 0

def test_logging_config():
"""Test logging configuration."""
logging.basicConfig()
logging.setLevel(logging.DEBUG)
logging.disable(logging.NOTSET)
return 0

def test_logger():
"""Test logger creation and usage."""
logger = logging.getLogger("myapp")
logger = logging.getLogger("myapp.submodule")
return 0

def test_handlers():
"""Test handler classes."""
handler = logging.StreamHandler
formatter = logging.Formatter
return 0

def test_log_with_level():
"""Test logging.log() with explicit level."""
logging.log(logging.INFO, "Message with explicit level")
return 0

def test_warn_alias():
"""Test warn as alias for warning."""
logging.warn("This uses warn alias")
warn_level = logging.WARN
return 0

def test_fatal_alias():
"""Test fatal as alias for critical."""
logging.fatal("This uses fatal alias")
fatal_level = logging.FATAL
return 0

def main():
"""Run all logging tests."""
test_log_levels()
test_basic_logging()
test_logging_config()
test_logger()
test_handlers()
test_log_with_level()
test_warn_alias()
test_fatal_alias()
return 0
49 changes: 49 additions & 0 deletions src/compiler/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1590,6 +1590,55 @@ pub fn emit_expr(
};
}
}

// Handle logging module functions
if module_name == "logging" {
if let Some(log_func) = crate::stdlib::logging::get_function(method_name) {
// Emit and drop all arguments
for arg in arguments {
let arg_type = emit_expr(arg, func, ctx, memory_layout, None);
match arg_type {
IRType::String => {
// Strings are (offset, length)
func.instruction(&Instruction::Drop);
func.instruction(&Instruction::Drop);
}
_ => {
func.instruction(&Instruction::Drop);
}
}
}

return match log_func {
crate::stdlib::logging::LoggingFunction::Debug
| crate::stdlib::logging::LoggingFunction::Info
| crate::stdlib::logging::LoggingFunction::Warning
| crate::stdlib::logging::LoggingFunction::Error
| crate::stdlib::logging::LoggingFunction::Critical
| crate::stdlib::logging::LoggingFunction::Exception
| crate::stdlib::logging::LoggingFunction::Log
| crate::stdlib::logging::LoggingFunction::BasicConfig
| crate::stdlib::logging::LoggingFunction::SetLevel
| crate::stdlib::logging::LoggingFunction::Disable
| crate::stdlib::logging::LoggingFunction::AddHandler
| crate::stdlib::logging::LoggingFunction::RemoveHandler => {
func.instruction(&Instruction::I32Const(0));
IRType::None
}
crate::stdlib::logging::LoggingFunction::GetLogger
| crate::stdlib::logging::LoggingFunction::Logger
| crate::stdlib::logging::LoggingFunction::Handler
| crate::stdlib::logging::LoggingFunction::StreamHandler
| crate::stdlib::logging::LoggingFunction::FileHandler
| crate::stdlib::logging::LoggingFunction::Formatter
| crate::stdlib::logging::LoggingFunction::Filter
| crate::stdlib::logging::LoggingFunction::LogRecord => {
func.instruction(&Instruction::I32Const(0));
IRType::Unknown
}
};
}
}
}
}

Expand Down
139 changes: 139 additions & 0 deletions src/stdlib/logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use crate::stdlib::StdlibValue;

/// Log levels as defined in Python's logging module
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(i32)]
pub enum LogLevel {
NotSet = 0,
Debug = 10,
Info = 20,
Warning = 30,
Error = 40,
Critical = 50,
}

impl LogLevel {
pub fn from_i32(value: i32) -> Self {
match value {
0 => LogLevel::NotSet,
10 => LogLevel::Debug,
20 => LogLevel::Info,
30 => LogLevel::Warning,
40 => LogLevel::Error,
50 => LogLevel::Critical,
_ if value < 10 => LogLevel::NotSet,
_ if value < 20 => LogLevel::Debug,
_ if value < 30 => LogLevel::Info,
_ if value < 40 => LogLevel::Warning,
_ if value < 50 => LogLevel::Error,
_ => LogLevel::Critical,
}
}

pub fn name(&self) -> &'static str {
match self {
LogLevel::NotSet => "NOTSET",
LogLevel::Debug => "DEBUG",
LogLevel::Info => "INFO",
LogLevel::Warning => "WARNING",
LogLevel::Error => "ERROR",
LogLevel::Critical => "CRITICAL",
}
}
}

pub fn get_attribute(attr: &str) -> Option<StdlibValue> {
match attr {
// Log level constants
"NOTSET" => Some(StdlibValue::Int(LogLevel::NotSet as i32)),
"DEBUG" => Some(StdlibValue::Int(LogLevel::Debug as i32)),
"INFO" => Some(StdlibValue::Int(LogLevel::Info as i32)),
"WARNING" => Some(StdlibValue::Int(LogLevel::Warning as i32)),
"WARN" => Some(StdlibValue::Int(LogLevel::Warning as i32)), // Alias
"ERROR" => Some(StdlibValue::Int(LogLevel::Error as i32)),
"CRITICAL" => Some(StdlibValue::Int(LogLevel::Critical as i32)),
"FATAL" => Some(StdlibValue::Int(LogLevel::Critical as i32)), // Alias
_ => None,
}
}

pub fn get_function(func: &str) -> Option<LoggingFunction> {
match func {
// Logging functions
"debug" => Some(LoggingFunction::Debug),
"info" => Some(LoggingFunction::Info),
"warning" => Some(LoggingFunction::Warning),
"warn" => Some(LoggingFunction::Warning), // Alias
"error" => Some(LoggingFunction::Error),
"critical" => Some(LoggingFunction::Critical),
"fatal" => Some(LoggingFunction::Critical), // Alias
"log" => Some(LoggingFunction::Log),
"exception" => Some(LoggingFunction::Exception),

// Configuration functions
"basicConfig" => Some(LoggingFunction::BasicConfig),
"getLogger" => Some(LoggingFunction::GetLogger),
"setLevel" => Some(LoggingFunction::SetLevel),
"disable" => Some(LoggingFunction::Disable),

// Handler/Formatter functions
"addHandler" => Some(LoggingFunction::AddHandler),
"removeHandler" => Some(LoggingFunction::RemoveHandler),

// Classes (used as constructors)
"Logger" => Some(LoggingFunction::Logger),
"Handler" => Some(LoggingFunction::Handler),
"StreamHandler" => Some(LoggingFunction::StreamHandler),
"FileHandler" => Some(LoggingFunction::FileHandler),
"Formatter" => Some(LoggingFunction::Formatter),
"Filter" => Some(LoggingFunction::Filter),
"LogRecord" => Some(LoggingFunction::LogRecord),

_ => None,
}
}

#[derive(Debug, Clone)]
pub enum LoggingFunction {
// Logging functions
Debug,
Info,
Warning,
Error,
Critical,
Log,
Exception,

// Configuration
BasicConfig,
GetLogger,
SetLevel,
Disable,

// Handler management
AddHandler,
RemoveHandler,

// Classes
Logger,
Handler,
StreamHandler,
FileHandler,
Formatter,
Filter,
LogRecord,
}

impl LoggingFunction {
pub fn log_level(&self) -> Option<LogLevel> {
match self {
LoggingFunction::Debug => Some(LogLevel::Debug),
LoggingFunction::Info => Some(LogLevel::Info),
LoggingFunction::Warning => Some(LogLevel::Warning),
LoggingFunction::Error => Some(LogLevel::Error),
LoggingFunction::Critical => Some(LogLevel::Critical),
LoggingFunction::Exception => Some(LogLevel::Error),
_ => None,
}
}
}
3 changes: 3 additions & 0 deletions src/stdlib/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod datetime;
pub mod functools;
pub mod itertools;
pub mod json;
pub mod logging;
pub mod math;
pub mod os;
pub mod random;
Expand All @@ -22,6 +23,7 @@ pub fn is_stdlib_module(name: &str) -> bool {
| "collections"
| "itertools"
| "functools"
| "logging"
)
}

Expand All @@ -37,6 +39,7 @@ pub fn get_stdlib_attributes(module: &str, attr: &str) -> Option<StdlibValue> {
"collections" => collections::get_attribute(attr),
"itertools" => itertools::get_attribute(attr),
"functools" => functools::get_attribute(attr),
"logging" => logging::get_attribute(attr),
_ => None,
}
}
Expand Down