class Detector::Python::HttpServer

Defined in:

detector/detectors/python/http_server.cr

Constant Summary

HANDLER_SUBCLASS_RE = /\bclass\s+\w+\s*\(\s*(?:[\w.]+\s*,\s*)*(?:[\w.]+\.)?(?:Base|Simple|CGI)HTTPRequestHandler\s*[,)]/

A handler subclass still counts on its own: a module that receives the base class through a re-export (from .compat import BaseHTTPRequestHandler) carries no http.server import of its own, and it is the file the analyzer needs. Anchored to a real class ...( header so a bare mention of the name in prose no longer qualifies. Earlier bases are allowed: class Handler(ThreadingMixIn, BaseHTTPRequestHandler) is the idiomatic way to write exactly the module this branch exists for.

IMPORT_RE = /(?:^|\n)[ \t\x{FEFF}]*(?:from[ \t]+http\.server[ \t]+import\b|import[ \t]+(?:[\w.]+[ \t]*,[ \t]*)*http\.server\b|from[ \t]+http[ \t]+import[ \t]+(?:[\w,][\w, \t]*[ \t,])?server\b)/

The stdlib module has to be imported before any of its handler or server classes can be named, so anchor on the import the way the other built-in-server detectors do (java_httpserver requires com.sun.net.httpserver, dart_http requires import 'dart:io').

This replaces four bare substring probes — "http.server", "HTTPServer", "BaseHTTPRequestHandler", "SimpleHTTPRequestHandler" — that matched anywhere in a file, including prose. Two real cases fell out of that:

  • a python -m http.server line in a comment or module docstring (the standard way to say "serve these fixtures locally") flagged the whole project as a stdlib-server app;
  • HTTPServer alone also matched Tornado's from tornado.httpserver import HTTPServer, so every Tornado project was additionally reported as python_http_server and paid for a second analyzer pass over the same tree.

Covers from http.server import ..., import [os, ]http.server[ as x] and from http import [cookies, ]server. \x{FEFF} after the line start keeps a UTF-8-BOM file's first line matchable — Noir::TextFile deliberately strips no BOM.

Class Method Summary

Instance Method Summary

Instance methods inherited from class Detector

applicable?(filename : String) : Bool applicable?, base_relative_path(filename : String) : String base_relative_path, content_matches?(file_contents : String, markers : Regex) : Bool content_matches?, detect(filename : String, file_contents : String) : Bool detect, gemfile_dependency?(file_contents : String, gem_name : String) : Bool gemfile_dependency?, gemspec_dependency?(file_contents : String, gem_name : String) : Bool gemspec_dependency?, idempotent? : Bool idempotent?, logger : NoirLogger logger, name : String name, path_sensitive? : Bool path_sensitive?, record_unparsable_document(filename : String, error : Exception) : Nil record_unparsable_document

Constructor methods inherited from class Detector

new(options : Hash(String, YAML::Any)) new

Macros inherited from class Detector

detector_for(tech, extensions = nil, basenames = nil, path_segments = nil, idempotent = nil) detector_for

Class Method Detail

def self.tech_name : String #

The tech name without needing an instance, so the registry can be read off the classes themselves rather than from a parallel list.


[View source]

Instance Method Detail

def applicable?(filename : String) : Bool #
Description copied from class Detector

Cheap filename-only filter the detector pass uses to skip #detect on files the detector cannot possibly match. The default true preserves prior behavior (every detector runs on every file). Override with the same predicate the body of #detect starts with — e.g., filename.ends_with?(".py") for a Python framework detector — so the detector loop avoids the #detect dispatch on files outside the detector's language.

On large codebases (saleor's 4255 .py files) this lifts ~100 virtual #detect calls per file out of the hot loop because most detectors' inner first-line is exactly this kind of cheap filename check.


[View source]
def detect(filename : String, file_contents : String) : Bool #

[View source]
def set_name #

[View source]