Skip to content

Commit 045b4c8

Browse files
Move pythonHandler from LogMsg to module level
This allows the constant to be declared the proper way, rather than using an undocumented feature of swig's %constant directive.
1 parent c50b558 commit 045b4c8

File tree

9 files changed

+177
-111
lines changed

9 files changed

+177
-111
lines changed

CHANGELOG.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Changes in v0.18.0:
3939
11/ Add __members__ attribute to exiv2 "data" structure types.
4040
12/ Deprecated keys(), values(), and items() methods and iterator of exiv2
4141
"data" structure types.
42+
13/ API CHANGE: exiv2.LogMsg.pythonHandler is replaced by exiv2.pythonHandler
4243

4344
Changes in v0.17.5:
4445
1/ Binary wheels incorporate libexiv2 v0.28.7.

src/doc/_templates/module.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{% if fullname == "exiv2._error" %}
22
{% set classes = classes + ['ErrorCode'] %}
3+
{% set attributes = attributes + ['pythonHandler'] %}
34
{% endif %}
45

56
{% if fullname == "exiv2._image" %}

src/doc/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def process_docstring(app, what, name, obj, options, lines):
9797
'Data_iterator', 'datum'), parts[3]), '']
9898
return
9999
# fixes for particular problems
100-
if name.endswith('error.LogMsg') or name.endswith('iptc.Iptcdatum'):
100+
if name.endswith('iptc.Iptcdatum'):
101101
# first line is not indented
102102
lines[0] = ' ' + lines[0]
103103
if name.endswith('XmpParser.initialize'):

src/interface/error.i

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
%module(package="exiv2") error
1919

2020
#ifndef SWIGIMPORTED
21-
%constant char* __doc__ = "Exiv2 error codes and log messages.";
21+
%constant char* __doc__ = "Exiv2 error codes and message logging.";
2222
#endif
2323

2424
%include "shared/preamble.i"
@@ -52,14 +52,38 @@ static void log_to_python(int level, const char* msg) {
5252
Exiv2::LogMsg::setHandler(&log_to_python);
5353
%}
5454

55-
// Provide Python logger and default logger as attributes of LogMsg
55+
// Replace LogMsg docs with something more relevant to Python
56+
%feature("docstring") Exiv2::LogMsg
57+
"Static class to control logging.
58+
59+
Applications can set the log level and change the log message handler.
60+
61+
The default handler :attr:`pythonHandler` sends messages to Python's
62+
:mod:`logging` system. Exiv2's handler :attr:`defaultHandler` sends
63+
messages to standard error. To change handler pass
64+
:attr:`exiv2.pythonHandler<pythonHandler>` or
65+
:attr:`exiv2.LogMsg.defaultHandler<defaultHandler>` to
66+
:meth:`setHandler`.
67+
68+
To disable logging entirely pass :obj:`None` to :meth:`setHandler`."
69+
70+
// Provide Python logger as attribute of module
71+
%constant Exiv2::LogMsg::Handler pythonHandler = &log_to_python;
72+
73+
// Provide default logger as attribute of LogMsg
5674
%extend Exiv2::LogMsg {
57-
%constant PyObject* pythonHandler = SWIG_NewFunctionPtrObj(
58-
(void*)log_to_python, SWIGTYPE_p_f_int_p_q_const__char__void);
59-
%constant PyObject* defaultHandler = SWIG_NewFunctionPtrObj(
60-
(void*)Exiv2::LogMsg::defaultHandler,
61-
SWIGTYPE_p_f_int_p_q_const__char__void);
75+
static const Exiv2::LogMsg::Handler defaultHandler;
6276
}
77+
// Adding static class attribute creates cvar and a getter function.
78+
// Hide them from the user interface.
79+
%pythoncode %{
80+
if __package__ or "." in __name__:
81+
from ._error import __all__
82+
else:
83+
from _error import __all__
84+
__all__.remove('LogMsg_defaultHandler_get')
85+
__all__.remove('cvar')
86+
%}
6387

6488
// Ignore anything that's unusable from Python
6589
%ignore Exiv2::AnyError;

src/swig-0_27_7/error.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,13 @@
1010
from ._error import *
1111
else:
1212
from _error import *
13+
14+
15+
if __package__ or "." in __name__:
16+
from ._error import __all__
17+
else:
18+
from _error import __all__
19+
__all__.remove('LogMsg_defaultHandler_get')
20+
__all__.remove('cvar')
21+
22+

src/swig-0_27_7/error_wrap.cxx

Lines changed: 60 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4491,10 +4491,34 @@ SWIGINTERN PyObject *_wrap_LogMsg_handler(PyObject *self, PyObject *args) {
44914491
}
44924492

44934493

4494+
SWIGINTERN int Swig_var_LogMsg_defaultHandler_set(PyObject *) {
4495+
SWIG_Error(SWIG_AttributeError,"Variable LogMsg_defaultHandler is read-only.");
4496+
return 1;
4497+
}
4498+
4499+
4500+
SWIGINTERN PyObject *Swig_var_LogMsg_defaultHandler_get(void) {
4501+
PyObject *pyobj = 0;
4502+
PyObject *self = 0;
4503+
4504+
(void)self;
4505+
pyobj = SWIG_NewFunctionPtrObj((void *)(Exiv2::LogMsg::defaultHandler), SWIGTYPE_p_f_int_p_q_const__char__void);
4506+
return pyobj;
4507+
}
4508+
4509+
4510+
SWIGINTERN PyObject *_wrap_LogMsg_defaultHandler_get(PyObject *SWIGUNUSEDPARM(self), PyObject *SWIGUNUSEDPARM(args)) {
4511+
return Swig_var_LogMsg_defaultHandler_get();
4512+
}
4513+
4514+
44944515
static PyMethodDef SwigMethods[] = {
4516+
{ "LogMsg_defaultHandler_get", _wrap_LogMsg_defaultHandler_get, METH_VARARGS, NULL},
44954517
{ NULL, NULL, 0, NULL }
44964518
};
44974519

4520+
static SwigPyGetSet LogMsg_defaultHandler_getset = { _wrap_LogMsg_defaultHandler_get, 0 };
4521+
static PyGetSetDef LogMsg_defaultHandler_getset_def = { (char *)"defaultHandler", SwigPyBuiltin_FunpackGetterClosure, 0, (char *)"Exiv2::LogMsg.defaultHandler", &LogMsg_defaultHandler_getset };
44984522
static SwigPyGetSet LogMsg___dict___getset = { SwigPyObject_get___dict__, 0 };
44994523
SWIGINTERN PyGetSetDef SwigPyBuiltin__Exiv2__LogMsg_getset[] = {
45004524
{ (char *)"__dict__", SwigPyBuiltin_FunpackGetterClosure, 0, (char *)"\n"
@@ -4578,31 +4602,18 @@ static PyHeapTypeObject SwigPyBuiltin__Exiv2__LogMsg_type = {
45784602
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */
45794603
#endif
45804604
"\n"
4581-
"Class for a log message, used by the library. Applications can set\n"
4582-
" the log level and provide a customer log message handler (callback\n"
4583-
" function).\n"
4584-
"\n"
4585-
" This class is meant to be used as a temporary object with the\n"
4586-
" related macro-magic like this:\n"
4605+
"Static class to control logging.\n"
45874606
"\n"
4588-
" ``\n"
4589-
" EXV_WARNING << \"Warning! Something looks fishy.\\n\";\n"
4590-
" ``\n"
4607+
"Applications can set the log level and change the log message handler.\n"
45914608
"\n"
4592-
" which translates to\n"
4609+
"The default handler :attr:`pythonHandler` sends messages to Python's\n"
4610+
":mod:`logging` system. Exiv2's handler :attr:`defaultHandler` sends\n"
4611+
"messages to standard error. To change handler pass\n"
4612+
":attr:`exiv2.pythonHandler<pythonHandler>` or\n"
4613+
":attr:`exiv2.LogMsg.defaultHandler<defaultHandler>` to\n"
4614+
":meth:`setHandler`.\n"
45934615
"\n"
4594-
" ``\n"
4595-
" if (LogMsg::warn >= LogMsg::level() && LogMsg::handler())\n"
4596-
" LogMsg(LogMsg::warn).os() << \"Warning! Something looks fishy.\\n\";\n"
4597-
" ``\n"
4598-
"\n"
4599-
" The macros EXV_DEBUG, EXV_INFO, EXV_WARNING and EXV_ERROR are\n"
4600-
" shorthands and ensure efficient use of the logging facility: If a\n"
4601-
" log message doesn't need to be generated because of the log level\n"
4602-
" setting, the temp object is not even created.\n"
4603-
"\n"
4604-
" Caveat: The entire log message is not processed in this case. So don't\n"
4605-
" make that call any logic that always needs to be executed.\n"
4616+
"To disable logging entirely pass :obj:`None` to :meth:`setHandler`.\n"
46064617
"", /* tp_doc */
46074618
(traverseproc) 0, /* tp_traverse */
46084619
(inquiry) 0, /* tp_clear */
@@ -4809,31 +4820,18 @@ static PyTypeObject *SwigPyBuiltin__Exiv2__LogMsg_type_create(PyTypeObject *type
48094820
{ Py_tp_is_gc, (void *)(inquiry) 0 },
48104821
{ Py_tp_del, (void *)(destructor) 0 },
48114822
{ Py_tp_doc, (void *)"\n"
4812-
"Class for a log message, used by the library. Applications can set\n"
4813-
" the log level and provide a customer log message handler (callback\n"
4814-
" function).\n"
4815-
"\n"
4816-
" This class is meant to be used as a temporary object with the\n"
4817-
" related macro-magic like this:\n"
4823+
"Static class to control logging.\n"
48184824
"\n"
4819-
" ``\n"
4820-
" EXV_WARNING << \"Warning! Something looks fishy.\\n\";\n"
4821-
" ``\n"
4825+
"Applications can set the log level and change the log message handler.\n"
48224826
"\n"
4823-
" which translates to\n"
4827+
"The default handler :attr:`pythonHandler` sends messages to Python's\n"
4828+
":mod:`logging` system. Exiv2's handler :attr:`defaultHandler` sends\n"
4829+
"messages to standard error. To change handler pass\n"
4830+
":attr:`exiv2.pythonHandler<pythonHandler>` or\n"
4831+
":attr:`exiv2.LogMsg.defaultHandler<defaultHandler>` to\n"
4832+
":meth:`setHandler`.\n"
48244833
"\n"
4825-
" ``\n"
4826-
" if (LogMsg::warn >= LogMsg::level() && LogMsg::handler())\n"
4827-
" LogMsg(LogMsg::warn).os() << \"Warning! Something looks fishy.\\n\";\n"
4828-
" ``\n"
4829-
"\n"
4830-
" The macros EXV_DEBUG, EXV_INFO, EXV_WARNING and EXV_ERROR are\n"
4831-
" shorthands and ensure efficient use of the logging facility: If a\n"
4832-
" log message doesn't need to be generated because of the log level\n"
4833-
" setting, the temp object is not even created.\n"
4834-
"\n"
4835-
" Caveat: The entire log message is not processed in this case. So don't\n"
4836-
" make that call any logic that always needs to be executed.\n"
4834+
"To disable logging entirely pass :obj:`None` to :meth:`setHandler`.\n"
48374835
"" },
48384836
{ Py_tp_repr, (void *)(reprfunc) 0 },
48394837
{ Py_tp_str, (void *)(reprfunc) 0 },
@@ -4957,6 +4955,7 @@ static swig_cast_info *swig_cast_initial[] = {
49574955
/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */
49584956

49594957
static swig_const_info swig_const_table[] = {
4958+
{ SWIG_PY_POINTER, "pythonHandler", 0, 0, (void *)(&log_to_python), &SWIGTYPE_p_f_int_p_q_const__char__void },
49604959
{0, 0, 0, 0.0, 0, 0}};
49614960

49624961
#ifdef __cplusplus
@@ -5419,7 +5418,7 @@ SWIG_init(void) {
54195418

54205419
SWIG_InstallConstants(d,swig_const_table);
54215420

5422-
SWIG_Python_SetConstant(d, d == md ? public_interface : NULL, "__doc__",SWIG_FromCharPtr("Exiv2 error codes and log messages."));
5421+
SWIG_Python_SetConstant(d, d == md ? public_interface : NULL, "__doc__",SWIG_FromCharPtr("Exiv2 error codes and message logging."));
54235422

54245423
if (strcmp(SWIG_name,"_error")) {
54255424
Python_Exiv2_ErrorCode = import_from_python("exiv2.""_error","ErrorCode");
@@ -5436,6 +5435,7 @@ SWIG_init(void) {
54365435
Exiv2::LogMsg::setHandler(&log_to_python);
54375436

54385437

5438+
54395439
Python_Exiv2_extras_create_enum = import_from_python("exiv2.extras","_create_enum");
54405440
if (!Python_Exiv2_extras_create_enum)
54415441
return INIT_ERROR_RETURN;
@@ -5465,16 +5465,25 @@ SWIG_init(void) {
54655465
if (!Python_enum_IntEnum)
54665466
return INIT_ERROR_RETURN;
54675467

5468-
SWIG_Python_SetConstant(d, d == md ? public_interface : NULL, "pythonHandler",SWIG_NewFunctionPtrObj(
5469-
(void*)log_to_python, SWIGTYPE_p_f_int_p_q_const__char__void));
5470-
SWIG_Python_SetConstant(d, d == md ? public_interface : NULL, "defaultHandler",SWIG_NewFunctionPtrObj(
5471-
(void*)Exiv2::LogMsg::defaultHandler,
5472-
SWIGTYPE_p_f_int_p_q_const__char__void));
5468+
globals = SWIG_globals();
5469+
if (!globals) {
5470+
PyErr_SetString(PyExc_TypeError, "Failure to create SWIG globals.");
5471+
#if PY_VERSION_HEX >= 0x03000000
5472+
return NULL;
5473+
#else
5474+
return;
5475+
#endif
5476+
}
5477+
PyDict_SetItemString(md, "cvar", globals);
5478+
SwigPyBuiltin_AddPublicSymbol(public_interface, "cvar");
54735479
SWIG_Python_SetConstant(d, d == md ? public_interface : NULL, "Level",Python_Exiv2_LogMsg_Level);
54745480
builtin_base_count = 0;
54755481
builtin_bases[builtin_base_count] = NULL;
54765482
PyDict_SetItemString(d, "this", this_descr);
54775483
PyDict_SetItemString(d, "thisown", thisown_descr);
5484+
static_getset = SwigPyStaticVar_new_getset(metatype, &LogMsg_defaultHandler_getset_def);
5485+
PyDict_SetItemString(d, static_getset->d_getset->name, (PyObject *)static_getset);
5486+
SWIG_Py_DECREF((PyObject *)static_getset);
54785487
builtin_pytype = SwigPyBuiltin__Exiv2__LogMsg_type_create(metatype, builtin_bases, d);
54795488
if(!builtin_pytype) {
54805489
#if PY_VERSION_HEX >= 0x03000000
@@ -5488,6 +5497,7 @@ SWIG_init(void) {
54885497
PyModule_AddObject(m, "LogMsg", (PyObject *)builtin_pytype);
54895498
SwigPyBuiltin_AddPublicSymbol(public_interface, "LogMsg");
54905499
d = md;
5500+
SWIG_addvarlink(globals, "LogMsg_defaultHandler", Swig_var_LogMsg_defaultHandler_get, Swig_var_LogMsg_defaultHandler_set);
54915501
#if PY_VERSION_HEX >= 0x03000000
54925502
return m;
54935503
#else

src/swig-0_28_7/error.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,13 @@
1010
from ._error import *
1111
else:
1212
from _error import *
13+
14+
15+
if __package__ or "." in __name__:
16+
from ._error import __all__
17+
else:
18+
from _error import __all__
19+
__all__.remove('LogMsg_defaultHandler_get')
20+
__all__.remove('cvar')
21+
22+

0 commit comments

Comments
 (0)