mergeron 2024.739079.12__tar.gz → 2024.739087.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mergeron might be problematic. Click here for more details.

Files changed (36) hide show
  1. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/PKG-INFO +1 -1
  2. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/pyproject.toml +2 -1
  3. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/__init__.py +1 -1
  4. mergeron-2024.739079.12/src/mergeron/core/excel_helper.py → mergeron-2024.739087.0/src/mergeron/core/xlsxw_helper.py +102 -35
  5. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/README.rst +0 -0
  6. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/License.txt +0 -0
  7. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/core/__init__.py +0 -0
  8. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/core/damodaran_margin_data.py +0 -0
  9. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/core/ftc_merger_investigations_data.py +0 -0
  10. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/core/guidelines_boundaries.py +0 -0
  11. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/core/guidelines_boundary_functions.py +0 -0
  12. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/core/guidelines_boundary_functions_extra.py +0 -0
  13. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/core/proportions_tests.py +0 -0
  14. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/core/pseudorandom_numbers.py +0 -0
  15. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/data/__init__.py +0 -0
  16. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/data/damodaran_margin_data.xls +0 -0
  17. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/data/damodaran_margin_data_dict.msgpack +0 -0
  18. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/data/ftc_invdata.msgpack +0 -0
  19. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/data/jinja2_LaTeX_templates/clrrate_cis_summary_table_template.tex.jinja2 +0 -0
  20. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_byhhianddelta_table_template.tex.jinja2 +0 -0
  21. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_summary_table_template.tex.jinja2 +0 -0
  22. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_summarypaired_table_template.tex.jinja2 +0 -0
  23. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/data/jinja2_LaTeX_templates/mergeron.cls +0 -0
  24. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/data/jinja2_LaTeX_templates/mergeron_table_collection_template.tex.jinja2 +0 -0
  25. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/data/jinja2_LaTeX_templates/setup_tikz_tables.tex +0 -0
  26. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/demo/__init__.py +0 -0
  27. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/demo/visualize_empirical_margin_distribution.py +0 -0
  28. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/ext/__init__.py +0 -0
  29. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/ext/tol_colors.py +0 -0
  30. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/gen/__init__.py +0 -0
  31. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/gen/_data_generation_functions.py +0 -0
  32. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/gen/data_generation.py +0 -0
  33. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/gen/enforcement_stats.py +0 -0
  34. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/gen/market_sample.py +0 -0
  35. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/gen/upp_tests.py +0 -0
  36. {mergeron-2024.739079.12 → mergeron-2024.739087.0}/src/mergeron/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mergeron
3
- Version: 2024.739079.12
3
+ Version: 2024.739087.0
4
4
  Summary: Merger Policy Analysis using Python
5
5
  License: MIT
6
6
  Keywords: merger policy analysis,merger guidelines,merger screening,policy presumptions,concentration standards,upward pricing pressure,GUPPI
@@ -13,7 +13,7 @@ keywords = [
13
13
  "upward pricing pressure",
14
14
  "GUPPI",
15
15
  ]
16
- version = "2024.739079.12"
16
+ version = "2024.739087.0"
17
17
 
18
18
  # Classifiers list: https://pypi.org/classifiers/
19
19
  classifiers = [
@@ -184,6 +184,7 @@ allow_redefinition = true
184
184
  plugins = "numpy.typing.mypy_plugin"
185
185
 
186
186
  [tool.pytest.ini_options]
187
+ log_auto_indent = 4
187
188
  minversion = "8.0"
188
189
  testpaths = ["tests"]
189
190
  addopts = ["--import-mode=importlib"]
@@ -11,7 +11,7 @@ from numpy.typing import NDArray
11
11
 
12
12
  _PKG_NAME: str = Path(__file__).parent.stem
13
13
 
14
- VERSION = "2024.739079.12"
14
+ VERSION = "2024.739087.0"
15
15
 
16
16
  __version__ = VERSION
17
17
 
@@ -112,15 +112,17 @@ def write_header(
112
112
  -------
113
113
  None
114
114
  """
115
- if not any((center_header, left_header, right_header)):
115
+ if any((center_header, left_header, right_header)):
116
+ _xl_sheet.set_header(
117
+ "".join([
118
+ f"&L{left_header}" if left_header else "",
119
+ f"&C{center_header}" if center_header else "",
120
+ f"&R{right_header}" if right_header else "",
121
+ ])
122
+ )
123
+
124
+ else:
116
125
  raise ValueError("must specify at least one header")
117
- _xl_sheet.set_footer(
118
- "".join([
119
- f"&L{left_header}" if left_header else "",
120
- f"&C{center_header}" if center_header else "",
121
- f"&{right_header}" if right_header else "",
122
- ])
123
- )
124
126
 
125
127
 
126
128
  def write_footer(
@@ -155,15 +157,16 @@ def write_footer(
155
157
  """
156
158
 
157
159
  if not any((center_footer, left_footer, right_footer)):
158
- raise ValueError("must specify at least one footer")
160
+ _xl_sheet.set_footer(
161
+ "".join([
162
+ f"&L{left_footer}" if left_footer else "",
163
+ f"&C{center_footer}" if center_footer else "",
164
+ f"&R{right_footer}" if right_footer else "",
165
+ ])
166
+ )
159
167
 
160
- _xl_sheet.set_footer(
161
- "".join([
162
- f"&L{left_footer}" if left_footer else "",
163
- f"&C{center_footer}" if center_footer else "",
164
- f"&{right_footer}" if right_footer else "",
165
- ])
166
- )
168
+ else:
169
+ raise ValueError("must specify at least one footer")
167
170
 
168
171
 
169
172
  def array_to_sheet(
@@ -174,7 +177,7 @@ def array_to_sheet(
174
177
  _col_id: int = 0,
175
178
  /,
176
179
  *,
177
- cell_format: Sequence[CFmt] | CFmt | None = None,
180
+ cell_format: Sequence[CFmt | Sequence[CFmt]] | CFmt | None = None,
178
181
  green_bar_flag: bool = True,
179
182
  ragged_flag: bool = True,
180
183
  ) -> tuple[int, int]:
@@ -184,6 +187,7 @@ def array_to_sheet(
184
187
  The given array is required be a two-dimensional array, whether
185
188
  a nested list, nested tuple, or a 2-D numpy ndarray.
186
189
 
190
+
187
191
  Parameters
188
192
  ----------
189
193
  _xl_book
@@ -207,10 +211,25 @@ def array_to_sheet(
207
211
  green_bar_flag
208
212
  Whether to highlight alternating rows as in green bar paper
209
213
 
214
+ ragged_flag
215
+ Whether to write ragged array, i.e. rows not all the same length
216
+ or not all cells are scalar-valued
217
+
218
+
210
219
  Raises
211
220
  ------
212
221
  ValueError
213
- If format tuple does not match data in length
222
+ If array is not two-dimensional
223
+
224
+ ValueError
225
+ If ragged_flag is False and array is not rectangular
226
+
227
+ ValueError
228
+ If array is not rectangular and cell_format is a Sequence
229
+
230
+ ValueError
231
+ If array is rectangular format tuple does not match data in length
232
+
214
233
 
215
234
  Returns
216
235
  -------
@@ -218,27 +237,57 @@ def array_to_sheet(
218
237
 
219
238
  """
220
239
 
240
+ if not ragged_flag:
241
+ try:
242
+ if np.ndim(_data_table) != 2:
243
+ raise ValueError("Given array must be two-dimensional.")
244
+ except ValueError as _err:
245
+ raise ValueError(
246
+ "Given array must be rectangular and homogenous, with scalar members."
247
+ " Alternatively, try with ragged_flag=True."
248
+ )
249
+ raise _err
250
+ elif not (
251
+ isinstance(_data_table, Sequence | np.ndarray)
252
+ and hasattr(_data_table[0], "__len__")
253
+ ):
254
+ raise ValueError("Given array must be two-dimensional array.")
255
+
221
256
  # Get the array dimensions and row and column numbers for Excel
222
257
  _num_rows = len(_data_table)
223
258
  _bottom_row_id = _row_id + _num_rows
224
259
  _num_cols = len(_data_table[0])
225
260
  _right_column_id = _col_id + _num_cols
226
261
 
227
- if isinstance(cell_format, tuple):
228
- ensure_cell_format_spec_tuple(cell_format)
229
- if not len(cell_format) == len(_data_table[0]):
262
+ if isinstance(cell_format, Sequence):
263
+ if ragged_flag:
264
+ raise ValueError(
265
+ "It is not clear whether the sequence of formats applies to all cells,"
266
+ " or to each cell respectively. Please provide a single-valued cell_format."
267
+ " Alternatively, you can iterate over the array using scalar_to_sheet()."
268
+ )
269
+ elif not len(cell_format) == len(_data_table[0]):
230
270
  raise ValueError("Format tuple does not match data in length.")
231
- _cell_format: Sequence[CFmt] = cell_format
271
+ ensure_cell_format_spec_tuple(cell_format)
272
+ _cell_format: Sequence[CFmt | Sequence[CFmt]] = cell_format
232
273
  elif isinstance(cell_format, CFmt):
233
274
  _cell_format = (cell_format,) * len(_data_table[0])
234
275
  else:
235
276
  _cell_format = (CFmt.XL_DEFAULT,) * len(_data_table[0])
236
277
 
278
+ # construct vector of xlslwrter.format.Format objects
279
+ _wbk_formats = tuple(xl_fmt(_xl_book, _cf) for _cf in _cell_format)
280
+ if _num_rows > 1:
281
+ _wbk_formats_greened = (
282
+ tuple(xl_fmt(_xl_book, (_cf, CFmt.BAR_FILL)) for _cf in _cell_format)
283
+ if green_bar_flag
284
+ else _wbk_formats
285
+ )
286
+
237
287
  for _ri, _rv in enumerate(_data_table):
288
+ _fmt_tuple = _wbk_formats_greened if _ri % 2 else _wbk_formats
238
289
  for _ci, _cv in enumerate(_rv):
239
- _cell_fmt = _cell_format[_ci] | (
240
- CFmt.BAR_FILL if green_bar_flag and _ri % 2 else {}
241
- )
290
+ _cell_fmt = _fmt_tuple[_ci]
242
291
  scalar_to_sheet(
243
292
  _xl_book, _xl_sheet, _row_id + _ri, _col_id + _ci, _cv, _cell_fmt
244
293
  )
@@ -303,18 +352,19 @@ def scalar_to_sheet(
303
352
  else:
304
353
  raise ValueError("Incorrect/incomplete specification for Excel cell data.")
305
354
 
355
+ _xl_fmt = xl_fmt(_xl_book, _cell_fmt)
306
356
  if isinstance(_cell_val, str):
307
- _xl_sheet.write_string(*_cell_addr, _cell_val, xl_fmt(_xl_book, _cell_fmt))
357
+ _xl_sheet.write_string(*_cell_addr, _cell_val, _xl_fmt)
308
358
  else:
309
359
  _xl_sheet.write(
310
- *_cell_addr,
311
- repr(_cell_val) if np.ndim(_cell_val) else _cell_val,
312
- xl_fmt(_xl_book, _cell_fmt),
360
+ *_cell_addr, repr(_cell_val) if np.ndim(_cell_val) else _cell_val, _xl_fmt
313
361
  )
314
362
 
315
363
 
316
364
  def xl_fmt(
317
- _xl_book: xlsxwriter.Workbook, _cell_fmt: Sequence[CFmt] | CFmt | None, /
365
+ _xl_book: xlsxwriter.Workbook,
366
+ _cell_fmt: Sequence[CFmt | Sequence[CFmt]] | CFmt | None,
367
+ /,
318
368
  ) -> xlsxwriter.format.Format:
319
369
  """
320
370
  Return :code:`xlsxwriter` `Format` object given a CFmt aenum, or tuple thereof.
@@ -327,25 +377,39 @@ def xl_fmt(
327
377
  _cell_fmt
328
378
  :code:`CFmt` aenum object, or tuple thereof
329
379
 
380
+ Raises
381
+ ------
382
+ ValueError
383
+ If format specification is not one of None, a CFmt aenum, or
384
+ a xlsxwriter.format.Format object
385
+
330
386
  Returns
331
387
  -------
332
388
  :code:`xlsxwriter` `Format` object
333
389
 
334
390
  """
391
+
392
+ if isinstance(_cell_fmt, xlsxwriter.format.Format):
393
+ return _cell_fmt
394
+ elif _cell_fmt is None:
395
+ return _xl_book.add_format(CFmt.XL_DEFAULT.value)
396
+
335
397
  _cell_fmt_dict: Mapping[str, Any] = {}
336
- if isinstance(_cell_fmt, tuple):
398
+ if isinstance(_cell_fmt, Sequence):
337
399
  ensure_cell_format_spec_tuple(_cell_fmt)
338
400
  for _cf in _cell_fmt:
339
401
  _cell_fmt_dict = _cell_fmt_dict | _cf.value
340
402
  elif isinstance(_cell_fmt, CFmt):
341
403
  _cell_fmt_dict = _cell_fmt.value
342
404
  else:
343
- _cell_fmt_dict = CFmt.XL_DEFAULT.value
405
+ raise ValueError("Improperly specified format specification.")
344
406
 
345
407
  return _xl_book.add_format(_cell_fmt_dict)
346
408
 
347
409
 
348
- def ensure_cell_format_spec_tuple(_cell_formats: Sequence[CFmt], /) -> None:
410
+ def ensure_cell_format_spec_tuple(
411
+ _cell_formats: Sequence[CFmt | Sequence[CFmt]], /
412
+ ) -> None:
349
413
  """
350
414
  Test that a given format specification is tuple of CFmt enums
351
415
 
@@ -357,7 +421,7 @@ def ensure_cell_format_spec_tuple(_cell_formats: Sequence[CFmt], /) -> None:
357
421
  Raises
358
422
  ------
359
423
  ValueError
360
- If format specification is not tuple of CFmt aenums
424
+ If format specification is not tuple of CFmt enums
361
425
 
362
426
  Returns
363
427
  -------
@@ -370,4 +434,7 @@ def ensure_cell_format_spec_tuple(_cell_formats: Sequence[CFmt], /) -> None:
370
434
  ensure_cell_format_spec_tuple(_cell_format)
371
435
 
372
436
  if not (isinstance(_cell_format, CFmt),):
373
- raise ValueError("Improperly specified format tuple.")
437
+ raise ValueError(
438
+ "Improperly specified format tuple for writing array."
439
+ " Must be tuple of CFmt enums."
440
+ )