From e68d5c3867a27199f3f6845a0a7fc52c89aa02d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Oct 2026 00:26:59 +0000 Subject: [PATCH 1/2] Initial plan From 34e2e0e7c66e077eb0e4197133d11040cd329d8d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Oct 2026 00:31:39 +0000 Subject: [PATCH 2/2] Apply remaining changes Co-authored-by: ebhills <53243273+ebhills@users.noreply.github.com> --- tests/recipes/test_write.py | 20 ++++++++++++++++++++ wrangles/recipe.py | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/tests/recipes/test_write.py b/tests/recipes/test_write.py index 0dd507e37..3671887eb 100644 --- a/tests/recipes/test_write.py +++ b/tests/recipes/test_write.py @@ -110,6 +110,26 @@ def test_specify_not_columns_regex(): df = wrangles.recipe.run(recipe, dataframe=data) assert df.columns == ['3col'] + +def test_specify_not_columns_missing_ignored(): + """ + Test writing and excluding a subset of columns while ignoring names that don't exist + """ + data = pd.DataFrame({ + 'col1': ['val1'], + 'col2': ['val2'] + }) + recipe = """ + write: + - dataframe: + not_columns: + - missing_col + - col2 + """ + df = wrangles.recipe.run(recipe, dataframe=data) + assert df.columns.tolist() == ['col1'] + + def test_specify_where(): """ Test writing and applying a WHERE sql criteria diff --git a/wrangles/recipe.py b/wrangles/recipe.py index bea3cdb8c..17c21f4d2 100644 --- a/wrangles/recipe.py +++ b/wrangles/recipe.py @@ -1104,12 +1104,37 @@ def _filter_dataframe( columns = _wildcard_expansion(df.columns.tolist(), columns) df = df[columns] - # Remove any columns specified by the user + # Remove any columns specified by the user. Missing names are ignored so + # not_columns behaves like a no-op for absent fields instead of raising. if not_columns: - not_columns = _wildcard_expansion(df.columns.tolist(), not_columns) - # List comprehension is used below to preserve order of columns - remaining_columns = [column for column in list(df.columns) if column not in not_columns] - df = df[remaining_columns] + if isinstance(not_columns, (str, int)): + not_columns = [not_columns] + else: + not_columns = list(not_columns) + + valid_not_columns = [] + for column in not_columns: + if column is None: + continue + + # Negative selectors are valid even when they don't match any current + # columns, because they are meant to remove matching columns if present. + if isinstance(column, str) and column.startswith('-'): + valid_not_columns.append(column) + continue + + try: + _wildcard_expansion(df.columns.tolist(), [column]) + except KeyError: + continue + + valid_not_columns.append(column) + + if valid_not_columns: + not_columns = _wildcard_expansion(df.columns.tolist(), valid_not_columns) + # List comprehension is used below to preserve order of columns + remaining_columns = [column for column in list(df.columns) if column not in not_columns] + df = df[remaining_columns] return df