adds early-out for model type checking based on path type - #1791
adds early-out for model type checking based on path type#1791JamesKunstle wants to merge 1 commit into
Conversation
4a277c7 to
7be48a9
Compare
| requires_files = { | ||
| "config.json", | ||
| "tokenizer.json", | ||
| "tokenizer.model", |
There was a problem hiding this comment.
removing this causes test to break because this is the only file that the test checks for.
7be48a9 to
aeac457
Compare
|
|
||
| if not model_path.is_file(): | ||
| logging.debug( | ||
| "The path to the model %s is not a file, and therefore cannot be a model in .gguf format.", |
There was a problem hiding this comment.
Also the repo tends to do: logger.debug(f"Message: {var}") for formatting.
There was a problem hiding this comment.
Linter wants me to use lazy logging via %s rather than f-strings
There was a problem hiding this comment.
lazy linter! let's stay consistent :)
There was a problem hiding this comment.
Okay no problem, just changed over. In the future we may want to standardize on lazy logging instead- I looked into it a bit and it seems to be a bit faster.
jaideepr97
left a comment
There was a problem hiding this comment.
+1 to seb and ali's comments
aeac457 to
6eb25d4
Compare
leseb
left a comment
There was a problem hiding this comment.
EOD for me here, assuming my last comment is addressed (change to use logger.debug(f"Message: {var}"), feel free to dismiss my review and merge. Thanks!
6eb25d4 to
35163b5
Compare
|
Funny, these changes are very similar to something I was dealing with in #1795 |
| Returns: | ||
| bool: True if the model is a safetensors model, False otherwise. | ||
| """ | ||
| if not model_path.is_dir(): |
There was a problem hiding this comment.
Just as a note - if a Path type isn't passed here, this still throw an exception as is_dir() is a method of the Path class: https://kodify.net/python/check-path-is-directory/#use-pathis_dir-or-ospathisdir
Not blocking, but you may also consider os.path.isdir() - the differences in behavior are outlined in that link above.
There was a problem hiding this comment.
Looks good, though this will throw an exception if that casting fails
There was a problem hiding this comment.
Wouldn't NotADirectoryError handler catch the situation? Why do we need to handle this explicitly here, again?
There was a problem hiding this comment.
With my suggestion above, we ensure a Path type is being passed, so agreed with @booxter here
There was a problem hiding this comment.
I think you're right @booxter. Explicitly checking if a path is a directory above is redundant since we catch an informative exception. I'll make a change reflective of that.
| # Third Party | ||
| from gguf.constants import GGUF_MAGIC | ||
|
|
||
| if not model_path.is_file(): |
There was a problem hiding this comment.
Similar story here to my comment above: https://www.freecodecamp.org/news/how-to-check-if-a-file-exists-in-python/
There was a problem hiding this comment.
Same comment as above
There was a problem hiding this comment.
Same code snippet suggestion here, given the above conversation
if not isinstance(model_path, pathlib.Path):
raise TypeError("'model_path' must be of type 'pathlib.Path')There was a problem hiding this comment.
Then below, can do something similar to the safetensors func
try:
with open(model_path, "rb") as f:
# Memory-map the file on the first 4 bytes (this is where the magic number is)
mmapped_file = mmap.mmap(f.fileno(), length=4, access=mmap.ACCESS_READ)
# Read the first 4 bytes
first_four_bytes = mmapped_file.read(4)
# Convert the first four bytes to an integer
first_four_bytes_int = int(struct.unpack("<I", first_four_bytes)[0])
# Close the memory-mapped file
mmapped_file.close()
return first_four_bytes_int == GGUF_MAGIC
except (FileNotFoundError, PermissionError) as e:
logger.debug("Failed to read file: %s", e)
return False@booxter let me know your thoughts here as well ^^
|
@nathan-weinberg Very good point. Since the function definition requires a pathlib.Path object, we could:
I'm going to do the first thing just in case. |
is_model_safetensors and is_model_gguf were raising exceptions rather than returning False for common failure cases. Signed-off-by: James Kunstle <jkunstle@redhat.com>
35163b5 to
fa9f21e
Compare
|
@nathan-weinberg would you mind please dismissing requested changes- I made the requested change. |
Those aren't mine, they're @leseb |
|
@nathan-weinberg He OK'ed dismissing if I updated the changes, I don't want to dismiss them myself as bad practice |
|
|
||
| # guards against users passing str-type paths if not | ||
| # statically analyzing. | ||
| if isinstance(model_path, str): |
There was a problem hiding this comment.
I disagree with the premise of this change - assuming callers may pass something but a Path. Instead of adding ad-hoc conversion code like proposed here, the callers should be forced to pass the expected types, if not already. The latter can also be enforced by adding more typing checks up the call stack.
Otherwise, why not handling model_path being a None, or some other type? This is a slippery slope.
There was a problem hiding this comment.
Definitely see your point. We can drop this if it's truly a bad route. It's a modest degree of leniency in this codebase because there's some heterogeneity on path handling- some modules might want to call this method that use str's representing paths. Since those representations are very close, I'm inclined to massage the input into a format that this function wants. Otherwise, if it's not something that's reasonably handle-able, it's rejected because it's NotADirectory.
There was a problem hiding this comment.
Elsewhere, we were ripping off some of these "special handling for string paths", so I'd like to avoid this push-pull. The strategic direction I think is to convert all code that attempts to pass strings where Paths are expected to actually pass Paths. (Same for other types.) These are code bugs, so they should be fixed in-place, not worked around.
There was a problem hiding this comment.
I propose the following snippet
if not isinstance(model_path, pathlib.Path):
raise TypeError("'model_path' must be of type 'pathlib.Path')This remains in the spirit of my comment, James's implementation, and Ihar's feedback.
There was a problem hiding this comment.
I thought a bit about this offline- I think @booxter's route of trusting the function definition (that the input is a pathlib.Path) is the right way to go, otherwise we'd be writing input type-validation code for every function. We're using type annotations so anyone who passes a str to this method should be warned by their development environment that this isn't a good way to do things.
| Returns: | ||
| bool: True if the model is a safetensors model, False otherwise. | ||
| """ | ||
| if not model_path.is_dir(): |
There was a problem hiding this comment.
Wouldn't NotADirectoryError handler catch the situation? Why do we need to handle this explicitly here, again?
|
@booxter's feedback is solid, doing what I'm suggesting here wouldn't really help. I'll close this PR and we can reopen if there are objections. |
| logger.debug("'model_path' was passed as 'str'. Casting to pathlib.Path") | ||
| model_path = pathlib.Path(model_path) | ||
|
|
||
| if not model_path.is_file(): |
There was a problem hiding this comment.
@JamesKunstle I think this change may actually be useful, since right now if a directory is passed, the function will raise an exception (on open). The rest of changes here, as you confirmed, are probably not a good idea. (Thanks.)
An alternative to is_file check could be catching misc exceptions expected from open, and returning False on any of them.
is_model_safetensorsandis_model_ggufwere raising exceptions rather than returning False for common file-based failure cases.