Skip to content

近代的な Python 3 へのリファクタリングと CI/ツール刷新 - #117

Open
takano32 wants to merge 8 commits into
masterfrom
modernize-python3-idioms
Open

takano32 wants to merge 8 commits into
masterfrom
modernize-python3-idioms

Conversation

@takano32

@takano32 takano32 commented Jun 27, 2026

Copy link
Copy Markdown
Collaborator

概要

挙動を変えない範囲で、コードを近代的な Python 3 のイディオムに整理し、周辺ツールも刷新しました。

コード変更

種別 内容
型判定 type(x) is Tisinstance(x, T)
文字列 % / + 連結 → f-string
クリーンアップ 不要な # -*- coding: utf-8 -*- ヘッダ・(object) 基底クラスを削除
真偽値 is True / is False / is not True を素直な評価に
リソース open()with 文に、bare except:except Exception:
dead code HTMLParserunescape ハンドラを削除(convert_charrefs=True のため呼ばれず、かつ Py3.9 で unescape は削除済み)

バグ修正

  • subculture/redis.py: e.message(Python 3 では AttributeError)を str(e) に修正
  • SelfUpdateSubculture: os.system("make update_packages")subprocess.run([...]) に置換(シェル経由を排除)

ツール刷新

  • CI: Python 2.7 の Travis を廃止し、GitHub Actions(Python 3.12 + redis サービス + mecab)に置換
  • Makefile: setupvirtualenv から標準ライブラリの python3 -m venv
  • requirements.txt: Py3 標準ライブラリの ipaddress を削除
  • README / .gitignore: 現行手順・生成物に合わせて更新

レビュー対応(追記)

Lint 指摘への対応と、CI が動くようになったことで表面化した既存の問題の修正を追加しました。

  • flake8 対応: 本当に未使用の import を削除し、subculture/__init__.py には __all__ を定義して再エクスポートを明示。スタイル違反(E302/E265/E122/E713/E741/W293/W503)も修正
  • 互換 shim の削除: import 文だけの subculture/{doge,knower,media,retirement}.pylinguistic.py/misc.py の再インポート行を削除。リポジトリ内に参照はありません(外部スクリプトが旧パスを import している場合は f2b26d7 を revert してください)
  • MeCab を ipadic に固定: コードは ipadic の 9 フィールド素性形式を前提としているため、MeCab.Tagger(ipadic.MECAB_ARGS) を明示し、依存を unidic → ipadic(pip 版)に変更
  • TitleExtract の文字コード修正: guess_encoding で判定した content_encoding を無視して UTF-8 決め打ちで decode していたバグを修正(Shift-JIS / EUC-JP ページで UnicodeDecodeError になっていた)
  • Gaishutsu を JSON 保存に変更: decode_responses=True の接続に binary pickle を保存していたため GET 時に UnicodeDecodeError が発生していた問題を修正。Redis に残っている旧 pickle エントリは初回アクセス時に新形式で上書きされます(自己修復)
  • テスト整備: 死んだ外部 URL のテスト 4 件をコメントアウト(gizmodo=404、ロイター=401、Instagram=要ログイン、Google Photos=404)、FreeBSD のタイトル期待値を現行に更新、cp932 テストは CI から到達できない coocan.jp の代わりにローカル HTTP サーバでフィクスチャを配信する方式に変更
  • "text": null イベントで TypeError になる問題を修正: 'text' in n['message'] はキーの存在しか見ていないため pattern.search(None) に到達していた。値を isinstance(..., str) で確認するよう変更し、回帰テストを追加

Closes #112
Closes #111

検証

  • 全ソースの py_compile を確認
  • METAR / DogeDetailStatus の出力フォーマット等価性を個別に確認
  • flake8 0 件・全テスト green を GitHub Actions 上で確認済み

🤖 Generated with Claude Code

コードの挙動を変えない範囲で近代化したリファクタリング。

Code:
- isinstance(x, T) に統一(type(x) is T を廃止)
- f-string 化(%, + 連結を置換)
- 不要な `# -*- coding: utf-8 -*-` ヘッダと `(object)` 基底を削除
- `is True` / `is False` / `is not True` を真偽値の素直な評価に
- open() を with 文に、bare except を except Exception に
- HTMLParser の dead code(convert_charrefs で呼ばれない unescape ハンドラ)を削除
- バグ修正: redis.py の `e.message`(Py3 で AttributeError)を str(e) に
- os.system("make update_packages") を subprocess.run に置換

Tooling:
- Travis (Python 2.7) を GitHub Actions (Python 3.12 + redis + mecab) に置換
- Makefile setup を virtualenv から `python3 -m venv` に
- requirements.txt から不要な ipaddress(Py3 標準) を削除
- README を更新(GH Actions バッジ / 現行セットアップ手順)
- .gitignore に venv/pytest 生成物を追加

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@yuiseki
yuiseki self-requested a review June 29, 2026 00:18

@yuiseki yuiseki left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

CI で Lint が落ちている模様です。 Lint ルールを実装に揃えるなどの対応していただけるとありがたいです 🙏

takano32 and others added 6 commits August 19, 2026 23:15
- Remove genuinely unused imports (math, Subculture, KnowerLevelUpSubculture,
  TwitterScraperSubculture in sun.py; HitozumaSubculture in tests.py)
- Declare __all__ in subculture/__init__.py so flake8 recognizes the
  package-level imports as intentional re-exports (F401)
- Fix style issues: E302, E265, E122, E713, E741, W293, W503

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Delete subculture/{doge,knower,media,retirement}.py, which consisted
solely of re-imports from subculture.redis, and drop the leftover
re-imports at the top of linguistic.py and misc.py. Nothing in this
repository references these module paths; everything goes through the
subculture package root. Revert this commit if any external script
still imports the old paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
divide_wordclass and the backward/break dictionaries in SilentSubculture
assume ipadic's 9-field feature format, but mecab-python3 silently picks
unidic when the unidic package is installed, so every feature lookup
missed and SilentSubculture always returned None in CI. Pass
ipadic.MECAB_ARGS to every Tagger and swap the unidic dependency (and
its download step in CI/Docker/Makefile) for the pip ipadic package.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
get_element_title decoded content as UTF-8 regardless of the
content_encoding that fetch(guess_encoding=True) detected, so Shift-JIS
and EUC-JP pages raised UnicodeDecodeError. Decode with the detected
encoding (errors='replace') instead.

Also refresh the network-dependent tests: the FreeBSD handbook now
redirects to the documentation portal with a longer title, and the
gizmodo/reuters/instagram/google-photos fixtures are dead links or
behind a login, so comment those out like the other retired tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The redis connection uses decode_responses=True, which makes GET raise
UnicodeDecodeError on the binary pickle payloads Gaishutsu was storing,
so the 既出 check has been dead under Python 3. Store JSON instead, and
treat undecodable or unparsable legacy entries as unseen so they get
overwritten in the new format on first touch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nomenclator.la.coocan.jp is not reachable from GitHub Actions runners,
so the test always saw an empty response in CI. Serve an equivalent
Shift-JIS page from a local HTTP server instead, which keeps the
encoding-detection regression coverage deterministic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@takano32
takano32 requested a review from yuiseki August 19, 2026 14:41
Slack/Lingr payloads can carry "text": null, and the 'text' in
n['message'] guard only checks key presence, so pattern.search(None)
raised TypeError: expected string or bytes-like object. Check the value
with isinstance instead and add a regression test.

Closes #111

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@takano32

Copy link
Copy Markdown
Collaborator Author

@yuiseki レビューありがとうございます。ご指摘の Lint は、ルールを実装に揃える形で対応しました(本当に未使用の import は削除、subculture/__init__.py の再エクスポートは __all__ で明示)。

あわせて、Lint が通ったことで表面化した既存のテスト失敗も一通り修正し、CI は green になっています(MeCab の辞書を ipadic に明示、TitleExtract の文字コード decode 修正、Gaishutsu の pickle→JSON 化、死んだ外部 URL テストの整理など。詳細は description に追記済みです)。#112#111 もこの PR で閉じる想定です。

お手すきの際に再レビューをお願いします 🙏

@yuiseki yuiseki left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM!!

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

Labels

None yet

Projects

None yet

2 participants