dataframe-textual 2.1.0__py3-none-any.whl → 2.2.0__py3-none-any.whl

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.
@@ -46,6 +46,9 @@ def cli() -> argparse.Namespace:
46
46
  parser.add_argument(
47
47
  "-I", "--no-inference", action="store_true", help="Do not infer data types when reading CSV/TSV"
48
48
  )
49
+ parser.add_argument(
50
+ "-t", "--truncate-ragged-lines", action="store_true", help="Truncate ragged lines when reading CSV/TSV"
51
+ )
49
52
  parser.add_argument("-E", "--ignore-errors", action="store_true", help="Ignore errors when reading CSV/TSV")
50
53
  parser.add_argument(
51
54
  "-c", "--comment-prefix", nargs="?", const="#", help="Comment lines are skipped when reading CSV/TSV"
@@ -94,6 +97,7 @@ def main() -> None:
94
97
  skip_rows_after_header=args.skip_rows_after_header,
95
98
  null_values=args.null,
96
99
  ignore_errors=args.ignore_errors,
100
+ truncate_ragged_lines=args.truncate_ragged_lines,
97
101
  )
98
102
  app = DataFrameViewer(*sources)
99
103
  app.run()
@@ -484,6 +484,7 @@ def load_dataframe(
484
484
  skip_rows_after_header: int = 0,
485
485
  null_values: list[str] | None = None,
486
486
  ignore_errors: bool = False,
487
+ truncate_ragged_lines: bool = False,
487
488
  ) -> list[Source]:
488
489
  """Load DataFrames from file specifications.
489
490
 
@@ -548,6 +549,7 @@ def load_dataframe(
548
549
  skip_rows_after_header=skip_rows_after_header,
549
550
  null_values=null_values,
550
551
  ignore_errors=ignore_errors,
552
+ truncate_ragged_lines=truncate_ragged_lines,
551
553
  )
552
554
  )
553
555
 
@@ -624,6 +626,7 @@ def load_file(
624
626
  schema_overrides: dict[str, pl.DataType] | None = None,
625
627
  null_values: list[str] | None = None,
626
628
  ignore_errors: bool = False,
629
+ truncate_ragged_lines: bool = False,
627
630
  ) -> list[Source]:
628
631
  """Load a single file.
629
632
 
@@ -678,6 +681,7 @@ def load_file(
678
681
  schema_overrides=schema_overrides,
679
682
  null_values=null_values,
680
683
  ignore_errors=ignore_errors,
684
+ truncate_ragged_lines=truncate_ragged_lines,
681
685
  )
682
686
  data.append(Source(lf, filename, filepath.stem))
683
687
  elif file_format in ("xlsx", "xls", "excel"):
@@ -110,6 +110,20 @@ class ReplaceState:
110
110
  done: bool = False # Whether the replace operation is complete
111
111
 
112
112
 
113
+ def add_rid_column(df: pl.DataFrame) -> pl.DataFrame:
114
+ """Add internal row index as last column to the dataframe if not already present.
115
+
116
+ Args:
117
+ df: The Polars DataFrame to modify.
118
+
119
+ Returns:
120
+ The modified DataFrame with the internal row index column added.
121
+ """
122
+ if RID not in df.columns:
123
+ df = df.lazy().with_row_index(RID).select(pl.exclude(RID), RID).collect()
124
+ return df
125
+
126
+
113
127
  class DataFrameTable(DataTable):
114
128
  """Custom DataTable to highlight row/column labels based on cursor position."""
115
129
 
@@ -321,7 +335,7 @@ class DataFrameTable(DataTable):
321
335
  super().__init__(**kwargs)
322
336
 
323
337
  # DataFrame state
324
- self.dataframe = df.lazy().with_row_index(RID).select(pl.exclude(RID), RID).collect() # Original dataframe
338
+ self.dataframe = add_rid_column(df) # Original dataframe
325
339
  self.df = self.dataframe # Internal/working dataframe
326
340
  self.filename = filename or "untitled.csv" # Current filename
327
341
  self.tabname = tabname or Path(filename).stem # Tab name
@@ -279,6 +279,14 @@ class DataFrameViewer(App):
279
279
  def action_duplicate_tab(self) -> None:
280
280
  """Duplicate the currently active tab.
281
281
 
282
+ Creates a copy of the current tab with the same data and filename.
283
+ The new tab is named with '_copy' suffix and inserted after the current tab.
284
+ """
285
+ self.do_duplicate_tab()
286
+
287
+ def do_duplicate_tab(self) -> None:
288
+ """Duplicate the currently active tab.
289
+
282
290
  Creates a copy of the current tab with the same data and filename.
283
291
  The new tab is named with '_copy' suffix and inserted after the current tab.
284
292
  """
@@ -320,6 +328,17 @@ class DataFrameViewer(App):
320
328
  Cycles through tabs by the specified offset. With offset=1, moves to next tab.
321
329
  With offset=-1, moves to previous tab. Wraps around when reaching edges.
322
330
 
331
+ Args:
332
+ offset: Number of tabs to advance (+1 for next, -1 for previous). Defaults to 1.
333
+ """
334
+ self.do_next_tab(offset)
335
+
336
+ def do_next_tab(self, offset: int = 1) -> None:
337
+ """Switch to the next tab or previous tab.
338
+
339
+ Cycles through tabs by the specified offset. With offset=1, moves to next tab.
340
+ With offset=-1, moves to previous tab. Wraps around when reaching edges.
341
+
323
342
  Args:
324
343
  offset: Number of tabs to advance (+1 for next, -1 for previous). Defaults to 1.
325
344
  """
@@ -478,7 +497,7 @@ class DataFrameViewer(App):
478
497
  """Handle the "save before closing?" confirmation."""
479
498
  if result:
480
499
  # User wants to save - close after save dialog opens
481
- active_table.do_save_to_file(title="Save Current Tab", task_after_save="close_tab")
500
+ active_table.do_save_to_file(task_after_save="close_tab")
482
501
  elif result is None:
483
502
  # User cancelled - do nothing
484
503
  return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataframe-textual
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Interactive terminal viewer/editor for tabular data
5
5
  Project-URL: Homepage, https://github.com/need47/dataframe-textual
6
6
  Project-URL: Repository, https://github.com/need47/dataframe-textual.git
@@ -0,0 +1,14 @@
1
+ dataframe_textual/__init__.py,sha256=E53fW1spQRA4jW9grxSqPEmoe9zofzr6twdveMbt_W8,1310
2
+ dataframe_textual/__main__.py,sha256=xXeUA2EqVhufPkTbvv6MOCt3_ESHBH3PsCE--07a0ww,3613
3
+ dataframe_textual/common.py,sha256=ZDhAT13UCgwtQborPbcHboX956A6N-m1-YvCpZHFtwY,28108
4
+ dataframe_textual/data_frame_help_panel.py,sha256=UEtj64XsVRdtLzuwOaITfoEQUkAfwFuvpr5Npip5WHs,3381
5
+ dataframe_textual/data_frame_table.py,sha256=sxs2AZ6UUmiPc6yf503Vvj3FoASgcQuDSec17pN1I80,149069
6
+ dataframe_textual/data_frame_viewer.py,sha256=fyrMSH2dFxc3h3HkJSaT1jBCQyoUUstGVXXPSG6wyjA,23099
7
+ dataframe_textual/sql_screen.py,sha256=P3j1Fv45NIKEYo9adb7NPod54FaU-djFIvCUMMHbvjY,7534
8
+ dataframe_textual/table_screen.py,sha256=XPzJI6FXjwnxtQSMTmluygwkYM-0-Lx3v9o-MuL6bMg,19071
9
+ dataframe_textual/yes_no_screen.py,sha256=NI7Zt3rETDWYiT5CH_FDy7sIWkZ7d7LquaZZbX79b2g,26400
10
+ dataframe_textual-2.2.0.dist-info/METADATA,sha256=yf7TJe5fHtrSlTl6nI3PkpzE80aTW-zuJKsBbZ0q0pQ,29160
11
+ dataframe_textual-2.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
12
+ dataframe_textual-2.2.0.dist-info/entry_points.txt,sha256=R_GoooOxcq6ab4RaHiVoZ4zrZJ-phMcGmlL2rwqncW8,107
13
+ dataframe_textual-2.2.0.dist-info/licenses/LICENSE,sha256=AVTg0gk1X-LHI-nnHlAMDQetrwuDZK4eypgSMDO46Yc,1069
14
+ dataframe_textual-2.2.0.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- dataframe_textual/__init__.py,sha256=E53fW1spQRA4jW9grxSqPEmoe9zofzr6twdveMbt_W8,1310
2
- dataframe_textual/__main__.py,sha256=vgHjpSsHBF34UN46iMgu_EiebZUzxWwJZ_ngOU3nQvI,3412
3
- dataframe_textual/common.py,sha256=gpNNY5ZePJkJPf-YoLSCHKaBpdXQ1yW2iSKYV6zZYUo,27908
4
- dataframe_textual/data_frame_help_panel.py,sha256=UEtj64XsVRdtLzuwOaITfoEQUkAfwFuvpr5Npip5WHs,3381
5
- dataframe_textual/data_frame_table.py,sha256=pPddPOa3hV3voCf3_9botg0jeNcPdWsN7Oiw5vKdMdU,148701
6
- dataframe_textual/data_frame_viewer.py,sha256=pX1xU3REQnMMEHapYKmr_FH1hFGExqTrOjjFWBx7dtg,22406
7
- dataframe_textual/sql_screen.py,sha256=P3j1Fv45NIKEYo9adb7NPod54FaU-djFIvCUMMHbvjY,7534
8
- dataframe_textual/table_screen.py,sha256=XPzJI6FXjwnxtQSMTmluygwkYM-0-Lx3v9o-MuL6bMg,19071
9
- dataframe_textual/yes_no_screen.py,sha256=NI7Zt3rETDWYiT5CH_FDy7sIWkZ7d7LquaZZbX79b2g,26400
10
- dataframe_textual-2.1.0.dist-info/METADATA,sha256=6RZnWlkm6mWKnQNMUTUGBeklOFYXQgWtd1lOJmz5oX0,29160
11
- dataframe_textual-2.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
12
- dataframe_textual-2.1.0.dist-info/entry_points.txt,sha256=R_GoooOxcq6ab4RaHiVoZ4zrZJ-phMcGmlL2rwqncW8,107
13
- dataframe_textual-2.1.0.dist-info/licenses/LICENSE,sha256=AVTg0gk1X-LHI-nnHlAMDQetrwuDZK4eypgSMDO46Yc,1069
14
- dataframe_textual-2.1.0.dist-info/RECORD,,