Skip to content

Conversation

@pizeroLOL
Copy link
Owner

@pizeroLOL pizeroLOL commented Oct 25, 2025

Sourcery 总结

重构库以使用 Pydantic 模型进行 CSES 课表的健壮验证和解析,用统一的 CSES 类替换旧有的解析器/生成器,并更新项目元数据和文档工具

新功能:

  • 添加 CSES 类,利用基于 Pydantic 的模型来解析和表示 CSES 课程文件
  • 在 cses.structures 中引入结构化数据模型(Subject, Lesson, WeekType, SingleDaySchedule, Schedule)
  • 在 cses.errors 中添加自定义错误类型(ParseError, VersionError),并在 cses.utils 中添加实用函数 week_num

改进:

  • 将解析器和生成器彻底重构为统一的加载工作流
  • 更新 README,描述 cseslib4py、从 pycses 重构以及上游仓库链接
  • 将 Python 要求提高到 >=3.9 并添加 pydantic 依赖
  • 包含 CSES 格式的示例 YAML 文件和 JSON 模式

文档:

  • 设置 Sphinx 文档,包括配置、Makefile 和 Read the Docs 集成
Original summary in English

Summary by Sourcery

Refactor the library to use Pydantic models for robust validation and parsing of CSES schedules, replace legacy parser/generator with a unified CSES class, update project metadata and documentation tooling

New Features:

  • Add CSES class leveraging Pydantic-based models for parsing and representing CSES course files
  • Introduce structured data models (Subject, Lesson, WeekType, SingleDaySchedule, Schedule) in cses.structures
  • Add custom error types (ParseError, VersionError) in cses.errors and utility function week_num in cses.utils

Enhancements:

  • Completely refactor parser and generator into a unified loading workflow
  • Update README to describe cseslib4py, refactoring from pycses, and link to upstream repo
  • Bump Python requirement to >=3.9 and add pydantic dependency
  • Include example YAML file and JSON schema for CSES format

Documentation:

  • Set up Sphinx documentation with configuration, Makefile, and Read the Docs integration

@sourcery-ai
Copy link

sourcery-ai bot commented Oct 25, 2025

审阅者指南

本次 PR 重构了 CSES Python 库:用统一的 CSES 类(由基于 pydantic 的数据模型支持)替换了遗留的解析器/生成器类,引入了自定义错误处理和实用函数,并全面修订了文档和打包。

新 CSES YAML 数据结构的 ER 图

erDiagram
    SUBJECTS {
        string name
        string simplified_name
        string teacher
        string room
    }
    SCHEDULES {
        string name
        int enable_day
        string weeks
    }
    CLASSES {
        string subject
        string start_time
        string end_time
    }
    SUBJECTS ||--o{ CLASSES : "referenced by"
    SCHEDULES ||--o{ CLASSES : "contains"
    SCHEDULES {
        string name
    }
    CLASSES {
        string subject
    }
Loading

新 CSES 数据模型及支持类型的类图

classDiagram
    class CSES {
        +schedule: list[SingleDaySchedule]
        +version: int
        +subjects: dict[str, Subject]
        +__init__(content: str)
        -_load(content: str)
    }
    class Subject {
        +name: str
        +simplified_name: str
        +teacher: str
        +room: str
    }
    class Lesson {
        +subject: Subject
        +start_time: datetime.time
        +end_time: datetime.time
    }
    class WeekType {
        <<enum>>
        +ALL
        +ODD
        +EVEN
    }
    class SingleDaySchedule {
        +enable_day: int
        +classes: list[Lesson]
        +name: str
        +weeks: WeekType
        +is_enabled_on_week(week: int): bool
        +is_enabled_on_day(start_day: datetime.date, day: datetime.date): bool
    }
    class Schedule {
        <<UserList[SingleDaySchedule]>>
        +__getitem__(index: int): SingleDaySchedule
    }
    CSES --> "*" SingleDaySchedule : schedule
    CSES --> "*" Subject : subjects
    SingleDaySchedule --> "*" Lesson : classes
    Lesson --> Subject : subject
    SingleDaySchedule --> WeekType : weeks
    Schedule --> "*" SingleDaySchedule
Loading

自定义错误类型的类图

classDiagram
    class CSESError {
        <<Exception>>
    }
    class ParseError {
        <<Exception>>
    }
    class VersionError {
        <<Exception>>
    }
    ParseError --|> CSESError
    VersionError --|> CSESError
Loading

遗留解析器/生成器移除和替换的类图

classDiagram
    class CSESParser {
        -file_path: str
        -data: dict
        -version: int
        -subjects: list
        -schedules: list
        -_load_file()
        -_parse_data()
        +get_subjects()
        +get_schedules()
        +get_schedule_by_day(day)
        +is_cses_file(file_path)
    }
    class CSESGenerator {
        -version: int
        -subjects: list
        -schedules: list
        +add_subject(...)
        +add_schedule(...)
        +generate_cses_data()
        +save_to_file(file_path)
    }
    class CSES {
    }
    CSES ..> CSESParser : "replacement for CSESParser"
    CSES ..> CSESGenerator : "replacement for CSESGenerator"
Loading

文件级别更改

更改 详情 文件
用新的 CSES 类替换遗留解析器和生成器
  • 移除了 CSESParser 和 CSESGenerator 的实现
  • 引入了 CSES 类,通过 _load 解析 YAML 内容
  • 通过 cses.structures 自动装配 pydantic 模型,并从 cses.errors 抛出错误
cses/__init__.py
为核心实体添加基于 pydantic 的数据结构
  • 在 structures.py 中定义了 Subject, Lesson, WeekType, SingleDaySchedule, Schedule
  • 实现了对日程的验证、排序和辅助方法
cses/structures.py
引入自定义错误类型和实用工具
  • 在 errors.py 中添加了 CSESError, ParseError, VersionError
  • 在 utils.py 中为周计算添加了 week_num 辅助函数
cses/errors.py
cses/utils.py
全面修订文档、示例和打包
  • 重写了 cseslib4py 的 README,并添加了示例 YAML 模式
  • 添加了 Sphinx 文档配置、make 脚本和 ReadTheDocs YAML
  • 更新了 setup.py(提高了 Python 要求,添加了 pydantic)和 requirements.txt
README.md
setup.py
requirements.txt
.readthedocs.yaml
docs/
cses_schema.json

提示和命令

与 Sourcery 互动

  • 触发新审查: 在拉取请求上评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 通过回复审查评论,请求 Sourcery 从中创建 issue。您也可以回复审查评论并加上 @sourcery-ai issue 来创建 issue。
  • 生成拉取请求标题: 随时在拉取请求标题中的任意位置写入 @sourcery-ai,即可生成标题。您也可以在拉取请求上评论 @sourcery-ai title 来随时(重新)生成标题。
  • 生成拉取请求摘要: 随时在拉取请求正文中的任意位置写入 @sourcery-ai summary,即可在您希望的位置生成 PR 摘要。您也可以在拉取请求上评论 @sourcery-ai summary 来随时(重新)生成摘要。
  • 生成审阅者指南: 在拉取请求上评论 @sourcery-ai guide,即可随时(重新)生成审阅者指南。
  • 解决所有 Sourcery 评论: 在拉取请求上评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果您已经处理了所有评论,并且不想再看到它们,这会很有用。
  • 驳回所有 Sourcery 审查: 在拉取请求上评论 @sourcery-ai dismiss,即可驳回所有现有的 Sourcery 审查。如果您想重新开始新的审查,这会特别有用——别忘了评论 @sourcery-ai review 来触发新的审查!

自定义您的体验

访问您的 仪表板 以:

  • 启用或禁用审查功能,例如 Sourcery 生成的拉取请求摘要、审阅者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

Original review guide in English

Reviewer's Guide

This PR refactors the CSES Python library: replacing legacy parser/generator classes with a unified CSES class backed by pydantic-based data models, introduces custom error handling and utility functions, and overhauls documentation and packaging.

ER diagram for new CSES YAML data structure

erDiagram
    SUBJECTS {
        string name
        string simplified_name
        string teacher
        string room
    }
    SCHEDULES {
        string name
        int enable_day
        string weeks
    }
    CLASSES {
        string subject
        string start_time
        string end_time
    }
    SUBJECTS ||--o{ CLASSES : "referenced by"
    SCHEDULES ||--o{ CLASSES : "contains"
    SCHEDULES {
        string name
    }
    CLASSES {
        string subject
    }
Loading

Class diagram for new CSES data model and supporting types

classDiagram
    class CSES {
        +schedule: list[SingleDaySchedule]
        +version: int
        +subjects: dict[str, Subject]
        +__init__(content: str)
        -_load(content: str)
    }
    class Subject {
        +name: str
        +simplified_name: str
        +teacher: str
        +room: str
    }
    class Lesson {
        +subject: Subject
        +start_time: datetime.time
        +end_time: datetime.time
    }
    class WeekType {
        <<enum>>
        +ALL
        +ODD
        +EVEN
    }
    class SingleDaySchedule {
        +enable_day: int
        +classes: list[Lesson]
        +name: str
        +weeks: WeekType
        +is_enabled_on_week(week: int): bool
        +is_enabled_on_day(start_day: datetime.date, day: datetime.date): bool
    }
    class Schedule {
        <<UserList[SingleDaySchedule]>>
        +__getitem__(index: int): SingleDaySchedule
    }
    CSES --> "*" SingleDaySchedule : schedule
    CSES --> "*" Subject : subjects
    SingleDaySchedule --> "*" Lesson : classes
    Lesson --> Subject : subject
    SingleDaySchedule --> WeekType : weeks
    Schedule --> "*" SingleDaySchedule
Loading

Class diagram for custom error types

classDiagram
    class CSESError {
        <<Exception>>
    }
    class ParseError {
        <<Exception>>
    }
    class VersionError {
        <<Exception>>
    }
    ParseError --|> CSESError
    VersionError --|> CSESError
Loading

Class diagram for legacy parser/generator removal and replacement

classDiagram
    class CSESParser {
        -file_path: str
        -data: dict
        -version: int
        -subjects: list
        -schedules: list
        -_load_file()
        -_parse_data()
        +get_subjects()
        +get_schedules()
        +get_schedule_by_day(day)
        +is_cses_file(file_path)
    }
    class CSESGenerator {
        -version: int
        -subjects: list
        -schedules: list
        +add_subject(...)
        +add_schedule(...)
        +generate_cses_data()
        +save_to_file(file_path)
    }
    class CSES {
    }
    CSES ..> CSESParser : "replacement for CSESParser"
    CSES ..> CSESGenerator : "replacement for CSESGenerator"
Loading

File-Level Changes

Change Details Files
Replace legacy parser and generator with a new CSES class
  • Removed CSESParser and CSESGenerator implementations
  • Introduced CSES class that parses YAML content via _load
  • Autowired pydantic models from cses.structures and raise errors from cses.errors
cses/__init__.py
Add pydantic-based data structures for core entities
  • Defined Subject, Lesson, WeekType, SingleDaySchedule, Schedule in structures.py
  • Implemented validation, sorting and helper methods on schedules
cses/structures.py
Introduce custom error types and utilities
  • Added CSESError, ParseError, VersionError in errors.py
  • Added week_num helper in utils.py for week calculations
cses/errors.py
cses/utils.py
Overhaul documentation, examples, and packaging
  • Rewrote README for cseslib4py and added example YAML schema
  • Added Sphinx docs config, make scripts and ReadTheDocs YAML
  • Updated setup.py (bumped python requirement, added pydantic), and requirements.txt
README.md
setup.py
requirements.txt
.readthedocs.yaml
docs/
cses_schema.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@pizeroLOL pizeroLOL merged commit e0f2292 into pizeroLOL:cseslib4py Oct 25, 2025
1 check was pending
Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嗨,我已审阅了你的更改——以下是一些反馈:

  • 在你的错误处理 f 字符串中,你在单引号字符串内使用了单引号(例如 {data['subjects']}),这将导致语法错误——请将外部引号切换为双引号或转义内部引号。
  • 考虑将 CSES.schedule 属性重命名为 schedules(或类似复数形式),以更好地反映它包含多个 SingleDaySchedule 对象并避免混淆。
  • 在加载 YAML 后添加显式检查,以确保 subjectsschedules 等必需的顶级键存在,这样你就可以快速失败并提供清晰的错误,而不是稍后引发 KeyError
给 AI 代理的提示
请处理此代码审查中的评论:

## 总体评论
- 在你的错误处理 f 字符串中,你在单引号字符串内使用了单引号(例如 `{data['subjects']}`),这将导致语法错误——请将外部引号切换为双引号或转义内部引号。
- 考虑将 `CSES.schedule` 属性重命名为 `schedules`(或类似复数形式),以更好地反映它包含多个 `SingleDaySchedule` 对象并避免混淆。
- 在加载 YAML 后添加显式检查,以确保 `subjects``schedules` 等必需的顶级键存在,这样你就可以快速失败并提供清晰的错误,而不是稍后引发 `KeyError`## 单独评论

### 评论 1
<location> `cses/__init__.py:52` </location>
<code_context>
+        data = yaml.safe_load(content)
+
+        # 版本处理&检查
+        self.version = data['version']
+        if self.version != 1:
+            raise err.VersionError(f'不支持的版本号: {self.version}')
</code_context>

<issue_to_address>
**issue:** 如果缺少 'version' 键,直接访问可能会引发 KeyError。

请使用 data.get('version') 并处理 None 情况,以避免 KeyError 并在缺少 'version' 时提供更清晰的错误消息。
</issue_to_address>

### 评论 2
<location> `cses/__init__.py:25-34` </location>
<code_context>
+            raise err.VersionError(f'不支持的版本号: {self.version}')
+
+        # 科目处理&检查
         try:
-            with open(file_path, 'r', encoding='utf-8') as f:
-                data = yaml.safe_load(f) 
-                return 'version' in data and 'subjects' in data and 'schedules' in data
-        except:
-            return False
-
-# 生成器
-class CSESGenerator:
-    def __init__(self, version=1):
-        """
-        初始化 CSES 生成器 
-        
-        Args:
-            version (int, optional): CSES 格式的版本号,默认为 1 
-        """
-        self.version  = version 
-        self.subjects  = []
-        self.schedules  = []
+            self.subjects = {s['name']: st.Subject(**s) for s in data['subjects']}
+        except st.ValidationError as e:
+            raise err.ParseError(f'科目数据有误: {data['subjects']}') from e
</code_context>

<issue_to_address>
**suggestion:** 科目构建假设所有必需字段都存在且有效。

在引发的 ParseError 中包含特定的验证错误详细信息(e.errors() 或 str(e))以改进调试。
</issue_to_address>

### 评论 3
<location> `cses/__init__.py:66` </location>
<code_context>
-        for cls in schedule['classes']:
-            print(f"- {cls['subject']} ({cls['start_time']} - {cls['end_time']})")
+            # 先构造课程列表,再构造课表
+            schedule_classes = {i['name']: i['classes'] for i in schedules}
+            built_lessons = {i['name']: [] for i in schedules}
+            for name, classes in schedule_classes.items():
</code_context>

<issue_to_address>
**issue:** 假设每个日程条目都存在 'name' 和 'classes' 键。

验证每个日程条目都包含 'name' 和 'classes' 键,以防止 KeyError 异常。
</issue_to_address>

### 评论 4
<location> `cses/__init__.py:70-71` </location>
<code_context>
+            built_lessons = {i['name']: [] for i in schedules}
+            for name, classes in schedule_classes.items():
+                for lesson in classes:
+                    built_lessons[name].append(
+                        st.Lesson(**(lesson | {'subject': self.subjects[lesson['subject']]}))
+                    )  # 从self.subjects中获取合法的Subject对象
+
</code_context>

<issue_to_address>
**issue:** 依赖 Python 3.9+ 字典联合运算符和科目查找。

如果 self.subjects 中缺少 lesson['subject'],将发生 KeyError。请添加错误处理以在这种情况下提供更清晰的消息。
</issue_to_address>

### 评论 5
<location> `cses/__init__.py:75-83` </location>
<code_context>
+                    enable_day=day['enable_day'],
+                    classes=built_lessons[day['name']],
+                    name=day['name'],
+                    weeks=st.WeekType(day['weeks']),
+                )
+                for day in schedules
</code_context>

<issue_to_address>
**suggestion (bug_risk):** WeekType 构造假设有效值。

捕获来自 st.WeekType(day['weeks']) 的 ValueError,并在遇到无效值时引发带有清晰消息的 ParseError。

```suggestion
            self.schedule = []
            for day in schedules:
                try:
                    week_type = st.WeekType(day['weeks'])
                except ValueError as ve:
                    raise err.ParseError(
                        f"Invalid weeks value '{day['weeks']}' for day '{day['name']}': {ve}"
                    ) from ve
                self.schedule.append(
                    st.SingleDaySchedule(
                        enable_day=day['enable_day'],
                        classes=built_lessons[day['name']],
                        name=day['name'],
                        weeks=week_type,
                    )
                )
```
</issue_to_address>

### 评论 6
<location> `cses/utils.py:26` </location>
<code_context>
+        >>> week_num(datetime.date(2025, 9, 1), datetime.date(2025, 10, 24))
+        8
+    """
+    return (day - start_day).days // 7 + 1
</code_context>

<issue_to_address>
**suggestion (bug_risk):** week_num 未验证 day >= start_day。

如果 day 在 start_day 之前,函数可能会返回 0 或负值。请添加检查以适当处理这种情况。
</issue_to_address>

### 评论 7
<location> `docs/source/conf.py:10` </location>
<code_context>
project = 'cseslib4py'
copyright = '2025, MacrosMeng'
author = 'MacrosMeng'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

import os
import sys

sys.path.insert(0, os.path.abspath('../../'))

extensions = [
    'sphinx.ext.napoleon',
    'sphinx.ext.autodoc',
    'sphinx.ext.viewcode',
]

templates_path = ['_templates']
exclude_patterns = []

language = 'zh-cn'

# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

html_theme = 'furo'
html_static_path = ['_static']

</code_context>

<issue_to_address>
**issue (code-quality):** 不要将值赋给内置变量 `copyright` ([`avoid-builtin-shadow`](https://docs.sourcery.ai/Reference/Default-Rules/comments/avoid-builtin-shadow/))

<br/><details><summary>Explanation</summary>Python 有许多 `builtin` 变量:构成语言一部分的函数和常量,例如 `list``getattr``type`(参见 https://docs.python.org/3/library/functions.html)。在语言中,重新绑定此类变量是有效的:

```python
list = [1, 2, 3]
```
但是,这被认为是不良实践。
- 它会混淆其他开发人员。
- 它会混淆语法高亮器和 linter。
- 这意味着你不能再将该内置函数用于其原始目的。

你如何解决这个问题?

将变量重命名为更具体的内容,例如 `integers`。在紧急情况下,`my_list` 和类似名称是约定俗成的占位符。</details>
</issue_to_address>

Sourcery 对开源免费 - 如果你喜欢我们的评论,请考虑分享它们 ✨
帮助我更有用!请点击每个评论上的 👍 或 👎,我将使用反馈来改进你的评论。
Original comment in English

Hey there - I've reviewed your changes - here's some feedback:

  • In your error handling f-strings you’re using single quotes inside single-quoted strings (e.g. {data['subjects']}), which will cause a syntax error—switch the outer quotes to double quotes or escape the inner ones.
  • Consider renaming the CSES.schedule attribute to schedules (or something plural) to better reflect that it holds multiple SingleDaySchedule objects and to avoid confusion.
  • Add an explicit check after loading YAML to ensure that required top-level keys like subjects and schedules exist, so you can fail fast with a clear error instead of raising a KeyError later.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In your error handling f-strings you’re using single quotes inside single-quoted strings (e.g. `{data['subjects']}`), which will cause a syntax error—switch the outer quotes to double quotes or escape the inner ones.
- Consider renaming the `CSES.schedule` attribute to `schedules` (or something plural) to better reflect that it holds multiple `SingleDaySchedule` objects and to avoid confusion.
- Add an explicit check after loading YAML to ensure that required top-level keys like `subjects` and `schedules` exist, so you can fail fast with a clear error instead of raising a `KeyError` later.

## Individual Comments

### Comment 1
<location> `cses/__init__.py:52` </location>
<code_context>
+        data = yaml.safe_load(content)
+
+        # 版本处理&检查
+        self.version = data['version']
+        if self.version != 1:
+            raise err.VersionError(f'不支持的版本号: {self.version}')
</code_context>

<issue_to_address>
**issue:** Direct access to 'version' key may raise KeyError if missing.

Use data.get('version') and handle the None case to avoid KeyError and provide a clearer error message if 'version' is missing.
</issue_to_address>

### Comment 2
<location> `cses/__init__.py:25-34` </location>
<code_context>
+            raise err.VersionError(f'不支持的版本号: {self.version}')
+
+        # 科目处理&检查
         try:
-            with open(file_path, 'r', encoding='utf-8') as f:
-                data = yaml.safe_load(f) 
-                return 'version' in data and 'subjects' in data and 'schedules' in data
-        except:
-            return False
-
-# 生成器
-class CSESGenerator:
-    def __init__(self, version=1):
-        """
-        初始化 CSES 生成器 
-        
-        Args:
-            version (int, optional): CSES 格式的版本号,默认为 1 
-        """
-        self.version  = version 
-        self.subjects  = []
-        self.schedules  = []
+            self.subjects = {s['name']: st.Subject(**s) for s in data['subjects']}
+        except st.ValidationError as e:
+            raise err.ParseError(f'科目数据有误: {data['subjects']}') from e
</code_context>

<issue_to_address>
**suggestion:** Subject construction assumes all required fields are present and valid.

Include the specific validation error details (e.errors() or str(e)) in the raised ParseError to improve debugging.
</issue_to_address>

### Comment 3
<location> `cses/__init__.py:66` </location>
<code_context>
-        for cls in schedule['classes']:
-            print(f"- {cls['subject']} ({cls['start_time']} - {cls['end_time']})")
+            # 先构造课程列表,再构造课表
+            schedule_classes = {i['name']: i['classes'] for i in schedules}
+            built_lessons = {i['name']: [] for i in schedules}
+            for name, classes in schedule_classes.items():
</code_context>

<issue_to_address>
**issue:** Assumes 'name' and 'classes' keys exist for every schedule entry.

Validate that each schedule entry contains both 'name' and 'classes' keys to prevent KeyError exceptions.
</issue_to_address>

### Comment 4
<location> `cses/__init__.py:70-71` </location>
<code_context>
+            built_lessons = {i['name']: [] for i in schedules}
+            for name, classes in schedule_classes.items():
+                for lesson in classes:
+                    built_lessons[name].append(
+                        st.Lesson(**(lesson | {'subject': self.subjects[lesson['subject']]}))
+                    )  # 从self.subjects中获取合法的Subject对象
+
</code_context>

<issue_to_address>
**issue:** Relies on Python 3.9+ dict union operator and subject lookup.

If lesson['subject'] is missing from self.subjects, a KeyError will occur. Please add error handling to provide a clearer message in this case.
</issue_to_address>

### Comment 5
<location> `cses/__init__.py:75-83` </location>
<code_context>
+                    enable_day=day['enable_day'],
+                    classes=built_lessons[day['name']],
+                    name=day['name'],
+                    weeks=st.WeekType(day['weeks']),
+                )
+                for day in schedules
</code_context>

<issue_to_address>
**suggestion (bug_risk):** WeekType construction assumes valid values.

Catch ValueError from st.WeekType(day['weeks']) and raise a ParseError with a clear message if an invalid value is encountered.

```suggestion
            self.schedule = []
            for day in schedules:
                try:
                    week_type = st.WeekType(day['weeks'])
                except ValueError as ve:
                    raise err.ParseError(
                        f"Invalid weeks value '{day['weeks']}' for day '{day['name']}': {ve}"
                    ) from ve
                self.schedule.append(
                    st.SingleDaySchedule(
                        enable_day=day['enable_day'],
                        classes=built_lessons[day['name']],
                        name=day['name'],
                        weeks=week_type,
                    )
                )
```
</issue_to_address>

### Comment 6
<location> `cses/utils.py:26` </location>
<code_context>
+        >>> week_num(datetime.date(2025, 9, 1), datetime.date(2025, 10, 24))
+        8
+    """
+    return (day - start_day).days // 7 + 1
</code_context>

<issue_to_address>
**suggestion (bug_risk):** week_num does not validate that day >= start_day.

If day is before start_day, the function may return 0 or negative values. Please add a check to handle this case appropriately.
</issue_to_address>

### Comment 7
<location> `docs/source/conf.py:10` </location>
<code_context>
project = 'cseslib4py'
copyright = '2025, MacrosMeng'
author = 'MacrosMeng'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

import os
import sys

sys.path.insert(0, os.path.abspath('../../'))

extensions = [
    'sphinx.ext.napoleon',
    'sphinx.ext.autodoc',
    'sphinx.ext.viewcode',
]

templates_path = ['_templates']
exclude_patterns = []

language = 'zh-cn'

# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

html_theme = 'furo'
html_static_path = ['_static']

</code_context>

<issue_to_address>
**issue (code-quality):** Don't assign to builtin variable `copyright` ([`avoid-builtin-shadow`](https://docs.sourcery.ai/Reference/Default-Rules/comments/avoid-builtin-shadow/))

<br/><details><summary>Explanation</summary>Python has a number of `builtin` variables: functions and constants that
form a part of the language, such as `list`, `getattr`, and `type`
(See https://docs.python.org/3/library/functions.html).
It is valid, in the language, to re-bind such variables:

```python
list = [1, 2, 3]
```
However, this is considered poor practice.
- It will confuse other developers.
- It will confuse syntax highlighters and linters.
- It means you can no longer use that builtin for its original purpose.

How can you solve this?

Rename the variable something more specific, such as `integers`.
In a pinch, `my_list` and similar names are colloquially-recognized
placeholders.</details>
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

data = yaml.safe_load(content)

# 版本处理&检查
self.version = data['version']
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: 如果缺少 'version' 键,直接访问可能会引发 KeyError。

请使用 data.get('version') 并处理 None 情况,以避免 KeyError 并在缺少 'version' 时提供更清晰的错误消息。

Original comment in English

issue: Direct access to 'version' key may raise KeyError if missing.

Use data.get('version') and handle the None case to avoid KeyError and provide a clearer error message if 'version' is missing.

Comment on lines +25 to +34
'物理': Subject(name='物理', simplified_name='物', teacher='赵军', room='104')}
"""

def __init__(self, content: str):
"""
根据星期获取课程安排
初始化 CSES。
Args:
day (str): 星期(如 'mon', 'tue' 等)
Returns:
list: 该星期的课程安排
content (str): CSES 课程文件的内容。
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: 科目构建假设所有必需字段都存在且有效。

在引发的 ParseError 中包含特定的验证错误详细信息(e.errors() 或 str(e))以改进调试。

Original comment in English

suggestion: Subject construction assumes all required fields are present and valid.

Include the specific validation error details (e.errors() or str(e)) in the raised ParseError to improve debugging.

for cls in schedule['classes']:
print(f"- {cls['subject']} ({cls['start_time']} - {cls['end_time']})")
# 先构造课程列表,再构造课表
schedule_classes = {i['name']: i['classes'] for i in schedules}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: 假设每个日程条目都存在 'name' 和 'classes' 键。

验证每个日程条目都包含 'name' 和 'classes' 键,以防止 KeyError 异常。

Original comment in English

issue: Assumes 'name' and 'classes' keys exist for every schedule entry.

Validate that each schedule entry contains both 'name' and 'classes' keys to prevent KeyError exceptions.

Comment on lines +70 to +71
built_lessons[name].append(
st.Lesson(**(lesson | {'subject': self.subjects[lesson['subject']]}))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: 依赖 Python 3.9+ 字典联合运算符和科目查找。

如果 self.subjects 中缺少 lesson['subject'],将发生 KeyError。请添加错误处理以在这种情况下提供更清晰的消息。

Original comment in English

issue: Relies on Python 3.9+ dict union operator and subject lookup.

If lesson['subject'] is missing from self.subjects, a KeyError will occur. Please add error handling to provide a clearer message in this case.

Comment on lines +75 to +83
self.schedule = [
st.SingleDaySchedule(
enable_day=day['enable_day'],
classes=built_lessons[day['name']],
name=day['name'],
weeks=st.WeekType(day['weeks']),
)
for day in schedules
]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): WeekType 构造假设有效值。

捕获来自 st.WeekType(day['weeks']) 的 ValueError,并在遇到无效值时引发带有清晰消息的 ParseError。

Suggested change
self.schedule = [
st.SingleDaySchedule(
enable_day=day['enable_day'],
classes=built_lessons[day['name']],
name=day['name'],
weeks=st.WeekType(day['weeks']),
)
for day in schedules
]
self.schedule = []
for day in schedules:
try:
week_type = st.WeekType(day['weeks'])
except ValueError as ve:
raise err.ParseError(
f"Invalid weeks value '{day['weeks']}' for day '{day['name']}': {ve}"
) from ve
self.schedule.append(
st.SingleDaySchedule(
enable_day=day['enable_day'],
classes=built_lessons[day['name']],
name=day['name'],
weeks=week_type,
)
)
Original comment in English

suggestion (bug_risk): WeekType construction assumes valid values.

Catch ValueError from st.WeekType(day['weeks']) and raise a ParseError with a clear message if an invalid value is encountered.

Suggested change
self.schedule = [
st.SingleDaySchedule(
enable_day=day['enable_day'],
classes=built_lessons[day['name']],
name=day['name'],
weeks=st.WeekType(day['weeks']),
)
for day in schedules
]
self.schedule = []
for day in schedules:
try:
week_type = st.WeekType(day['weeks'])
except ValueError as ve:
raise err.ParseError(
f"Invalid weeks value '{day['weeks']}' for day '{day['name']}': {ve}"
) from ve
self.schedule.append(
st.SingleDaySchedule(
enable_day=day['enable_day'],
classes=built_lessons[day['name']],
name=day['name'],
weeks=week_type,
)
)

>>> week_num(datetime.date(2025, 9, 1), datetime.date(2025, 10, 24))
8
"""
return (day - start_day).days // 7 + 1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): week_num 未验证 day >= start_day。

如果 day 在 start_day 之前,函数可能会返回 0 或负值。请添加检查以适当处理这种情况。

Original comment in English

suggestion (bug_risk): week_num does not validate that day >= start_day.

If day is before start_day, the function may return 0 or negative values. Please add a check to handle this case appropriately.

# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = 'cseslib4py'
copyright = '2025, MacrosMeng'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): 不要将值赋给内置变量 copyright (avoid-builtin-shadow)


ExplanationPython 有许多 builtin 变量:构成语言一部分的函数和常量,例如 listgetattrtype(参见 https://docs.python.org/3/library/functions.html)。在语言中,重新绑定此类变量是有效的:

list = [1, 2, 3]

但是,这被认为是不良实践。

  • 它会混淆其他开发人员。
  • 它会混淆语法高亮器和 linter。
  • 这意味着你不能再将该内置函数用于其原始目的。

你如何解决这个问题?

将变量重命名为更具体的内容,例如 integers。在紧急情况下,my_list 和类似名称是约定俗成的占位符。

Original comment in English

issue (code-quality): Don't assign to builtin variable copyright (avoid-builtin-shadow)


ExplanationPython has a number of builtin variables: functions and constants that
form a part of the language, such as list, getattr, and type
(See https://docs.python.org/3/library/functions.html).
It is valid, in the language, to re-bind such variables:

list = [1, 2, 3]

However, this is considered poor practice.

  • It will confuse other developers.
  • It will confuse syntax highlighters and linters.
  • It means you can no longer use that builtin for its original purpose.

How can you solve this?

Rename the variable something more specific, such as integers.
In a pinch, my_list and similar names are colloquially-recognized
placeholders.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants