codecov-io: ## [Current coverage][1] is `98.07%`
> Merging **#85** into **master** will increase coverage by **+0.05%** as of [`ea3e9e6`][3]


```diff
@@            master     #85   diff @@
======================================
  Files            1       1       
  Stmts          152     156     +4
  Branches         0       0       
  Methods          0       0       
======================================
+ Hit            149     153     +4
  Partial          0       0       
  Missed           3       3       
```

> Review entire [Coverage Diff][4] as of [`ea3e9e6`][3]


[1]: https://codecov.io/github/keleshev/schema?ref=ea3e9e6de11ed22e995df1b30ede806b9912bba5
[2]: https://codecov.io/github/keleshev/schema/features/suggestions?ref=ea3e9e6de11ed22e995df1b30ede806b9912bba5
[3]: https://codecov.io/github/keleshev/schema/commit/ea3e9e6de11ed22e995df1b30ede806b9912bba5
[4]: https://codecov.io/github/keleshev/schema/compare/e94b7144f3016654d1360eb1c070fd2db0d54a43...ea3e9e6de11ed22e995df1b30ede806b9912bba5

> Powered by [Codecov](https://codecov.io). Updated on successful CI builds.
sjakobi: Anybody know a better name than `callable_str`?

And does that function possibly need `_` as a prefix?
skorokithakis: I think that the method does need an underscore as a prefix, it's not meant to be exported by the module. Other than that, I'm ready to merge this.
skorokithakis: Oops, looks like there's a merge conflict. Can you resolve that so I can merge this?
diff --git a/schema.py b/schema.py
index 1ecf845..d24744d 100644
--- a/schema.py
+++ b/schema.py
@@ -70,7 +70,7 @@ class Use(object):
         except SchemaError as x:
             raise SchemaError([None] + x.autos, [self._error] + x.errors)
         except BaseException as x:
-            f = self._callable.__name__
+            f = _callable_str(self._callable)
             raise SchemaError('%s(%r) raised %r' % (f, data, x), self._error)
 
 
@@ -176,7 +176,7 @@ class Schema(object):
                 raise SchemaError('%r.validate(%r) raised %r' % (s, data, x),
                                   self._error)
         if flavor == CALLABLE:
-            f = s.__name__
+            f = _callable_str(s)
             try:
                 if s(data):
                     return data
@@ -211,3 +211,9 @@ class Optional(Schema):
                     '"%r" is too complex.' % (self._schema,))
             self.default = default
             self.key = self._schema
+
+
+def _callable_str(callable_):
+    if hasattr(callable_, '__name__'):
+        return callable_.__name__
+    return str(callable_)

doesn't work with operator.methodcaller
    from operator import methodcaller
    from schema import Schema

    f = methodcaller('endswith', '.csv')
    assert f('test.csv')

    Schema(f).validate('test.csv')

    AttributeError: 'operator.methodcaller' object has no attribute '__name__'

We can't assume that all callables have a `__name__`, it would seem.
diff --git a/test_schema.py b/test_schema.py
index ad49343..967dec0 100644
--- a/test_schema.py
+++ b/test_schema.py
@@ -1,5 +1,6 @@
 from __future__ import with_statement
 from collections import defaultdict, namedtuple
+from operator import methodcaller
 import os
 
 from pytest import raises
@@ -383,8 +384,18 @@ def test_missing_keys_exception_with_non_str_dict_keys():
         try:
             Schema({1: 'x'}).validate(dict())
         except SchemaError as e:
-            assert (e.args[0] ==
-                    "Missing keys: 1")
+            assert e.args[0] == "Missing keys: 1"
+            raise
+
+
+def test_issue_56_cant_rely_on_callables_to_have_name():
+    s = Schema(methodcaller('endswith', '.csv'))
+    assert s.validate('test.csv') == 'test.csv'
+    with SE:
+        try:
+            s.validate('test.py')
+        except SchemaError as e:
+            assert "operator.methodcaller" in e.args[0]
             raise
 
 

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-cov
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
coverage==7.8.0
exceptiongroup==1.2.2
iniconfig==2.1.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
pytest-cov==6.0.0
-e git+https://github.com/keleshev/schema.git@29286c1f9cce20cf70f2b15d9247f2ca6ef1d6c9#egg=schema
tomli==2.2.1

name: schema
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - iniconfig==2.1.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - tomli==2.2.1
prefix: /opt/conda/envs/schema

test_schema.py::test_issue_56_cant_rely_on_callables_to_have_name
test_schema.py::test_schema test_schema.py::test_validate_file test_schema.py::test_and test_schema.py::test_or test_schema.py::test_validate_list test_schema.py::test_list_tuple_set_frozenset test_schema.py::test_strictly test_schema.py::test_dict test_schema.py::test_dict_keys test_schema.py::test_dict_optional_keys test_schema.py::test_dict_optional_defaults test_schema.py::test_dict_subtypes test_schema.py::test_complex test_schema.py::test_nice_errors test_schema.py::test_use_error_handling test_schema.py::test_or_error_handling test_schema.py::test_and_error_handling test_schema.py::test_schema_error_handling test_schema.py::test_use_json test_schema.py::test_error_reporting test_schema.py::test_schema_repr test_schema.py::test_validate_object test_schema.py::test_issue_9_prioritized_key_comparison test_schema.py::test_issue_9_prioritized_key_comparison_in_dicts test_schema.py::test_missing_keys_exception_with_non_str_dict_keys test_schema.py::test_exception_handling_with_bad_validators
schema.py
schema.py