Should I use simple conditionals or a declarative registry to select file hash algorithms?

0
1
Asked By MellowCedar42 On

I'm writing a Python application whose main job is computing hashes for files. The hashing algorithm may depend on both the file's properties, such as its MIME type, and application configuration, such as a selected hash mode.

I'm considering a decorator-based, declarative design where hash functions register the conditions under which they apply:

```python
@hash_fn.default
def compute_bytes_hash(f: File) -> Hash:
"""Compute a hash from raw bytes."""

@hash_fn(mimetype="image/*", hash_type="perceptual")
def compute_image_hash(f: File) -> Hash:
"""Compute a perceptual image hash."""
```

This could make adding algorithms easy without changing the selection logic. However, supporting more complex rules would require building a flexible matching system for combinations of file properties and configuration values. That seems powerful but also potentially obscure and difficult to implement correctly. It would raise questions about overlapping rules, precedence, validation, and debugging.

The simpler alternative is an explicit selector:

```python
def choose_hash_fn(f: File, c: Config) -> HashFn:
if f.mimetype in mimetypes("image/*") and c.hash_type == "perceptual":
return compute_image_hash
return compute_bytes_hash
```

This is a personal project, and part of the goal is to learn, but my free time is limited. Maintainability, correctness, and readability matter to me. Which approach would you choose?

5 Answers

Answered By PlainOrbit7 On

Start with the explicit conditional selector. It is easy to read, test, type-check, and debug, and you can refactor later if the number of algorithms actually becomes difficult to manage. Designing a general rule engine before you know you need one is likely to add more complexity than value.

Answered By SwiftMaple28 On

A registry of strategy objects could be a reasonable middle ground if the simple selector grows. Each hasher can expose an `is_applicable(file, config)` method and a `compute` method, while a selector iterates through the registered algorithms. That keeps each algorithm’s matching logic near its implementation without requiring a full annotation language.

VividNook51 -

This gives you composability without committing to a complicated decorator DSL. Just make the ordering explicit so the result does not depend on import order or where a class happens to be defined.

Answered By ClearPine84 On

Decorators do not remove the need to decide which function applies; they mostly provide alternate syntax for registering that decision data. They can be pleasant to use, but writing and debugging the decorator machinery is harder than writing a straightforward selector. For a small application, the direct `if` statement is probably the best choice.

Answered By AmberTrellis36 On

The simple version is not inherently bad design. A clear, well-named selection function can remain maintainable for quite a few cases, especially if the conditions are covered by focused tests. Build the application first, then introduce a registry or strategy pattern only when real growth shows that the conditional logic is becoming a problem.

Answered By TabletopFox19 On

If you eventually need extensibility, model the system as a collection of matching rules and handlers rather than making decorators the central abstraction. Conceptually, it is a table of `(match_rule, handler)` entries. You can still use decorators or helper functions to populate that table later, but keeping the underlying data structure explicit makes precedence, overlaps, and testing much easier to reason about.

QuietHarbor63 -

The main issues to settle are what happens when multiple algorithms match, how their priority is determined, and how overlapping rules are detected. A self-registering system can make those details surprisingly difficult to see.

Related Questions

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.