internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: open-mmlab/mmcv

Issue:
mmcv.Config: obtain wrong value for some specific keys
Hi,

I'm using ``mmcv.Config`` to create my config object.
When I try to get the value of some specific keys, I receive the wrong values.
Here is an example:
```
>>> cfg = mmcv.Config(dict(filename='file.name', ck='hello')
>>> cfg.ck
hello
>>> cfg.filename
None
```

This is caused by https://github.com/open-mmlab/mmcv/blob/master/mmcv/utils/config.py#L200 that the name ``filename`` is preserved as a property of ``Config``.

Since words like ``filename`` and ``text`` are often used as dictionary keys, my suggestion is that we should avoid using these words as the property names of class ``Config``. 
Or at least, would you consider raise an error when a dictionary with keys like ``filename``is passed to ``Config``?


Discussion:
Hi @ycxioooong 
Thanks for reporting. I will open a PR to fix this issue. 

Resolving patch:
diff --git a/mmcv/utils/config.py b/mmcv/utils/config.py
--- a/mmcv/utils/config.py
+++ b/mmcv/utils/config.py
@@ -14,6 +14,7 @@
 
 BASE_KEY = '_base_'
 DELETE_KEY = '_delete_'
+RESERVED_KEYS = ['filename', 'text', 'pretty_text']
 
 
 class ConfigDict(Dict):
@@ -184,6 +185,9 @@ def __init__(self, cfg_dict=None, cfg_text=None, filename=None):
         elif not isinstance(cfg_dict, dict):
             raise TypeError('cfg_dict must be a dict, but '
                             f'got {type(cfg_dict)}')
+        for key in cfg_dict:
+            if key in RESERVED_KEYS:
+                raise KeyError(f'{key} is reserved for config file')
 
         super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict))
         super(Config, self).__setattr__('_filename', filename)


Test patch:
diff --git a/tests/data/config/g.py b/tests/data/config/g.py
new file mode 100644
--- /dev/null
+++ b/tests/data/config/g.py
@@ -0,0 +1 @@
+filename = 'reserved.py'
diff --git a/tests/test_config.py b/tests/test_config.py
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -266,3 +266,9 @@ def test_dump_mapping():
         text_cfg = Config.fromfile(text_cfg_filename)
 
     assert text_cfg._cfg_dict == cfg._cfg_dict
+
+
+def test_reserved_key():
+    cfg_file = osp.join(osp.dirname(__file__), 'data/config/g.py')
+    with pytest.raises(KeyError):
+        Config.fromfile(cfg_file)
Repository: open-mmlab/mmcv

Issue:
Bug feedback.
Thanks for your codes. 
In [mmcv/runner/base_runner](https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/base_runner.py#L385), 
`    def register_logger_hooks(self, log_config):
        log_interval = log_config['interval']
        for info in log_config['hooks']:
            logger_hook = mmcv.build_from_cfg(
                info, HOOKS, default_args=dict(interval=log_interval))
            self.register_hook(logger_hook, priority='VERY_LOW')
`
.
Only the argument 'interval' in log_config is fed into logger_hook. However, the argument 'by_epoch' is ignored, always true, so that if iter_based_runner is used, the logger will work unexpectedly. 


Discussion:
`logger_hook = mmcv.build_from_cfg(info, HOOKS, default_args=dict(interval=log_interval))`

Other arguments are in `info`.
Thanks.
I check for my issue carefully. In [mmcv/runner/iter_based_runner](https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/iter_based_runner.py#L224),
`        if log_config is not None:
            log_config.setdefault('by_epoch', False)
        self.register_logger_hooks(log_config)`, 
the key 'by_epoch' , similar to the key 'interval', is in `log_config`, instead of in `info`, which is one item of `log_config['hooks']`. 
In fact, I add `by_epoch` into default_args, the logger works expectedly.

Resolving patch:
diff --git a/mmcv/runner/iter_based_runner.py b/mmcv/runner/iter_based_runner.py
--- a/mmcv/runner/iter_based_runner.py
+++ b/mmcv/runner/iter_based_runner.py
@@ -222,5 +222,6 @@ def register_training_hooks(self,
         self.register_checkpoint_hook(checkpoint_config)
         self.register_hook(IterTimerHook())
         if log_config is not None:
-            log_config.setdefault('by_epoch', False)
+            for info in log_config['hooks']:
+                info.setdefault('by_epoch', False)
         self.register_logger_hooks(log_config)


Test patch:
Repository: open-mmlab/mmcv

Issue:
Bug in `register_training_hooks`
Here in [register_training_hooks](https://github.com/open-mmlab/mmcv/blob/1830347f8b23cf6177ff0d13973465366b51bfe5/mmcv/runner/base_runner.py#L395). The only positional args is 'lr_config', while 'log_config' is default to be None. But 'register_logger_hook' do not have None-handle like [others](https://github.com/open-mmlab/mmcv/blob/1830347f8b23cf6177ff0d13973465366b51bfe5/mmcv/runner/base_runner.py#L379). I recommand add the same two line handler.


Discussion:

Resolving patch:
diff --git a/mmcv/runner/base_runner.py b/mmcv/runner/base_runner.py
--- a/mmcv/runner/base_runner.py
+++ b/mmcv/runner/base_runner.py
@@ -386,6 +386,8 @@ def register_checkpoint_hook(self, checkpoint_config):
         self.register_hook(hook)
 
     def register_logger_hooks(self, log_config):
+        if log_config is None:
+            return
         log_interval = log_config['interval']
         for info in log_config['hooks']:
             logger_hook = mmcv.build_from_cfg(


Test patch:
Repository: open-mmlab/mmcv

Issue:
Forgot passing the argument pad_val in impad_to_multiple() to impad()
Hi, in the source code of **_impad_to_multiple()_**, the return value doesn't use the **_pad_val_** to _**impad()**_, which leads to the pad_val will always be 0 if call this function. 

`def impad_to_multiple(img, divisor, pad_val=0):
    """Pad an image to ensure each edge to be multiple to some number.

    Args:
        img (ndarray): Image to be padded.
        divisor (int): Padded image edges will be multiple to divisor.
        pad_val (Number | Sequence[Number]): Same as :func:`impad`.

    Returns:
        ndarray: The padded image.
    """
    pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor
    pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor
    return impad(img, shape=(pad_h, pad_w))`


Discussion:

Resolving patch:
diff --git a/mmcv/image/geometric.py b/mmcv/image/geometric.py
--- a/mmcv/image/geometric.py
+++ b/mmcv/image/geometric.py
@@ -459,7 +459,7 @@ def impad_to_multiple(img, divisor, pad_val=0):
     """
     pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor
     pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor
-    return impad(img, shape=(pad_h, pad_w))
+    return impad(img, shape=(pad_h, pad_w), pad_val=pad_val)
 
 
 def _get_shear_matrix(magnitude, direction='horizontal'):


Test patch:
Repository: open-mmlab/mmcv

Issue:
imshow_bboxes does not show bboxes if img is incontiguous 
As [DKDKDDK](https://stackoverflow.com/questions/57586449/why-cv2-rectangle-sometimes-return-np-ndarray-while-sometimes-cv2-umat) asked, cv2.rectangle seems unable to draw inplacely on incontiguous arrays. When calling `mmcv.imshow_bboxes` or `mmcv.imshow_det_bboxes`, the contiguousness of argument `img` is consistent with what the user passed in. Would it be convenient to add `img = np.ascontiguousarray(img)` inside `mmcv.imshow_bboxes` and `mmcv.imshow_det_bboxes`?


Discussion:
Hi @LutingWang, great suggestions. We'll consider adding this in next versions. @ZwwWayne could you please have a look?
Agree with that. @LutingWang , would you like to create a PR for that?
BTW, MMDetection just switches its visualization backend from OpenCV to matplotlib. We will also migrate it once we are certain that the API is stable for more general usages.

Resolving patch:
diff --git a/mmcv/visualization/image.py b/mmcv/visualization/image.py
--- a/mmcv/visualization/image.py
+++ b/mmcv/visualization/image.py
@@ -53,6 +53,7 @@ def imshow_bboxes(img,
         ndarray: The image with bboxes drawn on it.
     """
     img = imread(img)
+    img = np.ascontiguousarray(img)
 
     if isinstance(bboxes, np.ndarray):
         bboxes = [bboxes]
@@ -119,6 +120,7 @@ def imshow_det_bboxes(img,
     assert bboxes.shape[0] == labels.shape[0]
     assert bboxes.shape[1] == 4 or bboxes.shape[1] == 5
     img = imread(img)
+    img = np.ascontiguousarray(img)
 
     if score_thr > 0:
         assert bboxes.shape[1] == 5
@@ -129,7 +131,7 @@ def imshow_det_bboxes(img,
 
     bbox_color = color_val(bbox_color)
     text_color = color_val(text_color)
-    img = np.ascontiguousarray(img)
+
     for bbox, label in zip(bboxes, labels):
         bbox_int = bbox.astype(np.int32)
         left_top = (bbox_int[0], bbox_int[1])


Test patch:
Repository: open-mmlab/mmcv

Issue:
'ConvModule' object has no attribute 'norm' when using torch.jit.trace
environment: python3.6.6 pytorch1.7.0

code:

```
import torch
from mmcv.cnn.bricks import ConvModule

conv = ConvModule(3, 8, 2, norm_cfg=dict(type='BN'))
input_example = torch.randn((1, 3, 224, 224))

ts = torch.jit.trace(func=conv.eval(), example_inputs=input_example)
torch.jit.save(ts, 'conv_module.ts')
```

It work well, but when set `norm_cfg=None`, failed.

```
torch.nn.modules.module.ModuleAttributeError: 'ConvModule' object has no attribute 'norm'
```

Any help?


Discussion:

Resolving patch:
diff --git a/mmcv/cnn/bricks/conv_module.py b/mmcv/cnn/bricks/conv_module.py
--- a/mmcv/cnn/bricks/conv_module.py
+++ b/mmcv/cnn/bricks/conv_module.py
@@ -146,6 +146,8 @@ def __init__(self,
                 norm_channels = in_channels
             self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels)
             self.add_module(self.norm_name, norm)
+        else:
+            self.norm_name = None
 
         # build activation layer
         if self.with_activation:
@@ -162,7 +164,10 @@ def __init__(self,
 
     @property
     def norm(self):
-        return getattr(self, self.norm_name)
+        if self.norm_name:
+            return getattr(self, self.norm_name)
+        else:
+            return None
 
     def init_weights(self):
         # 1. It is mainly for customized conv layers with their own


Test patch:
diff --git a/tests/test_cnn/test_conv_module.py b/tests/test_cnn/test_conv_module.py
--- a/tests/test_cnn/test_conv_module.py
+++ b/tests/test_cnn/test_conv_module.py
@@ -75,7 +75,7 @@ def test_conv_module():
     assert conv.with_activation
     assert hasattr(conv, 'activate')
     assert not conv.with_norm
-    assert not hasattr(conv, 'norm')
+    assert conv.norm is None
     x = torch.rand(1, 3, 256, 256)
     output = conv(x)
     assert output.shape == (1, 8, 255, 255)
@@ -83,7 +83,7 @@ def test_conv_module():
     # conv
     conv = ConvModule(3, 8, 2, act_cfg=None)
     assert not conv.with_norm
-    assert not hasattr(conv, 'norm')
+    assert conv.norm is None
     assert not conv.with_activation
     assert not hasattr(conv, 'activate')
     x = torch.rand(1, 3, 256, 256)
Repository: open-mmlab/mmcv

Issue:
 DeformConv2d bias error
Cannot add bias attribute in https://github.com/open-mmlab/mmcv/blob/master/mmcv/ops/deform_conv.py#L237, Because the judgment condition of xx_init initialization logic is:

```python
if hasattr(module, 'bias') and module.bias is not None:
```
if module.bias=False, the above code will report an error:

```python
AttributeError: 'bool' object has no attribute 'fill_'
```

and please fix docstring https://github.com/open-mmlab/mmcv/blob/master/mmcv/ops/deform_conv.py#L203



Discussion:

Resolving patch:
diff --git a/mmcv/ops/deform_conv.py b/mmcv/ops/deform_conv.py
--- a/mmcv/ops/deform_conv.py
+++ b/mmcv/ops/deform_conv.py
@@ -200,7 +200,7 @@ class DeformConv2d(nn.Module):
             channels to output channels. Default: 1.
         deform_groups (int): Number of deformable group partitions.
         bias (bool): If True, adds a learnable bias to the output.
-            Default: True.
+            Default: False.
 
     """
 
@@ -234,7 +234,6 @@ def __init__(self,
         self.dilation = _pair(dilation)
         self.groups = groups
         self.deform_groups = deform_groups
-        self.bias = bias
         # enable compatibility with nn.Conv2d
         self.transposed = False
         self.output_padding = _single(0)
@@ -301,7 +300,8 @@ def __repr__(self):
         s += f'dilation={self.dilation},\n'
         s += f'groups={self.groups},\n'
         s += f'deform_groups={self.deform_groups},\n'
-        s += f'bias={self.bias})'
+        # bias is not supported in DeformConv2d.
+        s += 'deform_groups=False)'
         return s
 
 


Test patch:
diff --git a/tests/test_ops/test_deform_conv.py b/tests/test_ops/test_deform_conv.py
--- a/tests/test_ops/test_deform_conv.py
+++ b/tests/test_ops/test_deform_conv.py
@@ -1,4 +1,5 @@
 import numpy as np
+import pytest
 import torch
 
 input = [[[[1., 2., 3.], [0., 1., 2.], [3., 5., 2.]]]]
@@ -56,6 +57,20 @@ def _test_deformconv(self, dtype=torch.float, threshold=1e-3):
         assert np.allclose(model.weight.grad.detach().cpu().numpy(),
                            gt_deform_weight_grad, threshold)
 
+        from mmcv.ops import DeformConv2d
+        # test bias
+        model = DeformConv2d(1, 1, 2, stride=1, padding=0)
+        assert not hasattr(model, 'bias')
+        # test bias=True
+        with pytest.raises(AssertionError):
+            model = DeformConv2d(1, 1, 2, stride=1, padding=0, bias=True)
+        # test in_channels % group != 0
+        with pytest.raises(AssertionError):
+            model = DeformConv2d(3, 2, 3, groups=2)
+        # test out_channels % group != 0
+        with pytest.raises(AssertionError):
+            model = DeformConv2d(3, 4, 3, groups=3)
+
     def test_deformconv(self):
         self._test_deformconv(torch.double)
         self._test_deformconv(torch.float)
Repository: open-mmlab/mmcv

Issue:
SyncBatchNorm breaks after PyTorch 1.9.0
**Describe the Issue**
`torch.nn.SyncBatchNorm` has been updated since PyTorch 1.9.0 and `_specify_ddp_gpu_num()` is no longer available, breaking the following code snippet: https://github.com/open-mmlab/mmcv/blob/eb08835fa246ea81263eb25fbe2caa54ef11271c/mmcv/utils/parrots_wrapper.py#L83-L87

More details: https://github.com/open-mmlab/mmocr/issues/325


Discussion:

Resolving patch:
diff --git a/mmcv/cnn/bricks/norm.py b/mmcv/cnn/bricks/norm.py
--- a/mmcv/cnn/bricks/norm.py
+++ b/mmcv/cnn/bricks/norm.py
@@ -106,7 +106,7 @@ def build_norm_layer(cfg, num_features, postfix=''):
     cfg_.setdefault('eps', 1e-5)
     if layer_type != 'GN':
         layer = norm_layer(num_features, **cfg_)
-        if layer_type == 'SyncBN':
+        if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'):
             layer._specify_ddp_gpu_num(1)
     else:
         assert 'num_groups' in cfg_
diff --git a/mmcv/utils/parrots_wrapper.py b/mmcv/utils/parrots_wrapper.py
--- a/mmcv/utils/parrots_wrapper.py
+++ b/mmcv/utils/parrots_wrapper.py
@@ -82,10 +82,6 @@ def _get_norm():
 
 class SyncBatchNorm(SyncBatchNorm_):
 
-    def _specify_ddp_gpu_num(self, gpu_size):
-        if TORCH_VERSION != 'parrots':
-            super()._specify_ddp_gpu_num(gpu_size)
-
     def _check_input_dim(self, input):
         if TORCH_VERSION == 'parrots':
             if input.dim() < 2:


Test patch:
Repository: open-mmlab/mmcv

Issue:
A little annotation problem about deform convolution
In the `__repr__` magic method of `class DeformConv2d(nn.Module)` in [`deform_conv.py`](https://github.com/open-mmlab/mmcv/blob/master/mmcv/ops/deform_conv.py#L303-L315), there is         
```
    # bias is not supported in DeformConv2d.
    s += 'deform_groups=False)
```

Should it be
```
    # bias is not supported in DeformConv2d.
    s += 'bias=False)
```
?



Discussion:
yet, thanks for your feedback. I will create a PR to fix it.

Resolving patch:
diff --git a/mmcv/ops/deform_conv.py b/mmcv/ops/deform_conv.py
--- a/mmcv/ops/deform_conv.py
+++ b/mmcv/ops/deform_conv.py
@@ -311,7 +311,7 @@ def __repr__(self):
         s += f'groups={self.groups},\n'
         s += f'deform_groups={self.deform_groups},\n'
         # bias is not supported in DeformConv2d.
-        s += 'deform_groups=False)'
+        s += 'bias=False)'
         return s
 
 
diff --git a/mmcv/runner/hooks/evaluation.py b/mmcv/runner/hooks/evaluation.py
--- a/mmcv/runner/hooks/evaluation.py
+++ b/mmcv/runner/hooks/evaluation.py
@@ -29,7 +29,7 @@ class EvalHook(Hook):
         interval (int): Evaluation interval. Default: 1.
         by_epoch (bool): Determine perform evaluation by epoch or by iteration.
             If set to True, it will perform by epoch. Otherwise, by iteration.
-            default: True.
+            Default: True.
         save_best (str, optional): If a metric is specified, it would measure
             the best checkpoint during evaluation. The information about best
             checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep


Test patch:
Repository: open-mmlab/mmcv

Issue:
Support None in DictAction
Some times there are needs to override some key in the config to be `None`, e.g., the load_from is assigned to some string and you want to override it through `--cfg-options load_from=None`, it will pass a string of None rather than the real `None` value to the config and make checkpoint loader load checkpoint from a path named `'None'`



Discussion:

Resolving patch:
diff --git a/mmcv/utils/config.py b/mmcv/utils/config.py
--- a/mmcv/utils/config.py
+++ b/mmcv/utils/config.py
@@ -638,6 +638,8 @@ def _parse_int_float_bool(val):
             pass
         if val.lower() in ['true', 'false']:
             return True if val.lower() == 'true' else False
+        if val == 'None':
+            return None
         return val
 
     @staticmethod


Test patch:
diff --git a/tests/test_utils/test_config.py b/tests/test_utils/test_config.py
--- a/tests/test_utils/test_config.py
+++ b/tests/test_utils/test_config.py
@@ -470,9 +470,18 @@ def test_dict_action():
     with pytest.raises(AssertionError):
         parser.parse_args(['--options', 'item2.a=[(a,b), [1,2], false'])
     # Normal values
-    args = parser.parse_args(
-        ['--options', 'item2.a=1', 'item2.b=0.1', 'item2.c=x', 'item3=false'])
-    out_dict = {'item2.a': 1, 'item2.b': 0.1, 'item2.c': 'x', 'item3': False}
+    args = parser.parse_args([
+        '--options', 'item2.a=1', 'item2.b=0.1', 'item2.c=x', 'item3=false',
+        'item4=none', 'item5=None'
+    ])
+    out_dict = {
+        'item2.a': 1,
+        'item2.b': 0.1,
+        'item2.c': 'x',
+        'item3': False,
+        'item4': 'none',
+        'item5': None,
+    }
     assert args.options == out_dict
     cfg_file = osp.join(data_path, 'config/a.py')
     cfg = Config.fromfile(cfg_file)
Repository: open-mmlab/mmcv

Issue:
RandomFlip return wrong keypoint flip result
The current _flip_keypoint function in RandomFlip return the concatenate of keypoints and meta_info

https://github.com/open-mmlab/mmcv/blob/4ae327f43c2d2aab07aa084ca7ffc8f2095a280a/mmcv/transforms/processing.py#L1262

while the keypoints is the original input and is not fliped.

The correct code should be 
```
flipped = np.concatenate([flipped, meta_info], axis=-1)
```


Discussion:
Hi @zero0kiriyu , thanks for your report. Are you interested in making a PR to fix this issue?

Resolving patch:
diff --git a/mmcv/transforms/processing.py b/mmcv/transforms/processing.py
--- a/mmcv/transforms/processing.py
+++ b/mmcv/transforms/processing.py
@@ -1203,7 +1203,8 @@ def _flip_bbox(self, bboxes: np.ndarray, img_shape: Tuple[int, int],
             bboxes (numpy.ndarray): Bounding boxes, shape (..., 4*k)
             img_shape (tuple[int]): Image shape (height, width)
             direction (str): Flip direction. Options are 'horizontal',
-                'vertical'.
+                'vertical', and 'diagonal'.
+
         Returns:
             numpy.ndarray: Flipped bounding boxes.
         """
@@ -1239,7 +1240,8 @@ def _flip_keypoints(
             keypoints (numpy.ndarray): Keypoints, shape (..., 2)
             img_shape (tuple[int]): Image shape (height, width)
             direction (str): Flip direction. Options are 'horizontal',
-                'vertical'.
+                'vertical', and 'diagonal'.
+
         Returns:
             numpy.ndarray: Flipped keypoints.
         """
@@ -1259,7 +1261,7 @@ def _flip_keypoints(
             raise ValueError(
                 f"Flipping direction must be 'horizontal', 'vertical', \
                   or 'diagonal', but got '{direction}'")
-        flipped = np.concatenate([keypoints, meta_info], axis=-1)
+        flipped = np.concatenate([flipped, meta_info], axis=-1)
         return flipped
 
     def _flip_seg_map(self, seg_map: dict, direction: str) -> np.ndarray:
@@ -1357,6 +1359,7 @@ def transform(self, results: dict) -> dict:
 
         Args:
             results (dict): Result dict from loading pipeline.
+
         Returns:
             dict: Flipped results, 'img', 'gt_bboxes', 'gt_seg_map',
             'gt_keypoints', 'flip', and 'flip_direction' keys are


Test patch:
Repository: open-mmlab/mmcv

Issue:
typo in `imcrop` docstring
https://github.com/open-mmlab/mmcv/blob/abb600ad8d34c0d1bc88b01c4d2526ebdea1f8dc/mmcv/image/geometric.py#L442-L443

I believe `padding` should be `scaling` here.


Discussion:
Sorry for my late reply and thanks for your feedback, would mind posting a PR to fix this?
No, thanks. I think we could see the modification in the next release.

Resolving patch:
diff --git a/mmcv/image/geometric.py b/mmcv/image/geometric.py
--- a/mmcv/image/geometric.py
+++ b/mmcv/image/geometric.py
@@ -440,7 +440,7 @@ def imcrop(
         img (ndarray): Image to be cropped.
         bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes.
         scale (float, optional): Scale ratio of bboxes, the default value
-            1.0 means no padding.
+            1.0 means no scaling.
         pad_fill (Number | list[Number]): Value to be filled for padding.
             Default: None, which means no padding.
 


Test patch:
Repository: HybirdCorp/creme_crm

Issue:
management command 'geolocation --stat' failed
really not a blocking bug, but seems very easy to fix:
```
    self.sysout(f'{Town.objects.count()} town(s) in database.')
TypeError: sysout() missing 1 required positional argument: 'visible'
```

there is just a missing argument in self.sysout()


Discussion:

Resolving patch:
diff --git a/creme/geolocation/management/commands/geolocation.py b/creme/geolocation/management/commands/geolocation.py
--- a/creme/geolocation/management/commands/geolocation.py
+++ b/creme/geolocation/management/commands/geolocation.py
@@ -299,8 +299,8 @@ def import_town_all(self, verbosity=0):
             self.sysout(url, verbosity > 1)
             self.import_town_database(url, defaults)
 
-    def print_stats(self):
-        self.sysout(f'{Town.objects.count()} town(s) in database.')
+    def print_stats(self, verbosity=0):
+        self.sysout(f'{Town.objects.count()} town(s) in database.', verbosity > 0)
 
     def handle(self, *args, **options):
         populate = options.get('populate')
@@ -309,7 +309,7 @@ def handle(self, *args, **options):
         verbosity = options.get('verbosity')
 
         if stats:
-            self.print_stats()
+            self.print_stats(verbosity)
 
         if imports:
             self.import_town_all(verbosity)


Test patch:
Repository: HybirdCorp/creme_crm

Issue:
[WIP] fix python3.9.12 3.9.13 ci issue



Discussion:

Resolving patch:
diff --git a/creme/__init__.py b/creme/__init__.py
--- a/creme/__init__.py
+++ b/creme/__init__.py
@@ -1,5 +1,10 @@
 __version__ = '2.4-alpha1'
 
+
+def get_version():
+    return __version__
+
+
 # App registry hooking ---------------------------------------------------------
 
 try:


Test patch: