Process text sketch - #180

Open
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch
Open

Process text sketch#180
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch

Conversation

@ChristianGeng

@ChristianGengChristianGeng commented Nov 14, 2024

Copy link
Copy Markdown
Member

Summary by Sourcery

Enhance the processing of text and audio data by introducing a new series generator for index handling and refactoring the post-processing logic into a separate method. Update tests to cover new functionality and improve code maintainability.

Enhancements:

  • Refactor the process index function to use a new series generator for handling different index types.
  • Introduce a new method _postprocess_xs to handle post-processing of processed data, improving code reuse and readability.

Tests:

  • Add new test cases to cover the data_identity function and process_func parameter in the test_process_index function.

@sourcery-ai

sourcery-aiBot commented Nov 14, 2024

Copy link
Copy Markdown
Contributor

Reviewer's Guide by Sourcery

This PR implements improvements to text processing functionality, focusing on handling different types of data and indices in the audio processing pipeline. The changes include better handling of file types, improved index type management, and enhanced data processing capabilities.

Sequence diagram for process_file method

sequenceDiagram
participant Process
participant Utils
Process->>Utils: read_text(file, root)
Note right of Process: For text files
Process->>Utils: read_audio(file, start, end, root)
Note right of Process: For audio/video files
Process->>Process: _process_data(data, idx)
Process->>Process: _process_data(signal, sampling_rate, idx)
Loading

Updated class diagram for Process class

classDiagram
class Process {
+int num_workers
+bool multiprocessing
+bool verbose
+Callable read_func
+void _process_file(String file, ...)
+Series process_files(List files)
+Series process_folder(String folder)
+Series _process_index_wo_segment(Index index)
+static Series _postprocess_xs(List xs)
+Any _call_data(...)
+Dict _special_args(...)
}
note for Process "Added read_func attribute and updated methods to use it"
Loading

File-Level Changes

ChangeDetailsFiles
Added new data processing and index type handling functionality
  • Added helper function to determine index type based on preserve_index and segment flags
  • Implemented series generator function to handle different index types
  • Added support for process_func parameter in test_process_index
  • Enhanced handling of None values in index processing
tests/test_process_text.py
Enhanced Process class with improved file reading capabilities
  • Added read_func parameter to Process class initialization
  • Implemented dynamic setting of read_audio and read_text methods
  • Made sampling_rate parameter optional in identity function
  • Added postprocessing method for handling different data types
audinterface/core/process.py
Improved error handling and edge cases
  • Changed RuntimeError to FileNotFoundError for missing files
  • Added handling for dictionary and iterable text data types
  • Improved handling of None values in starts and ends lists
  • Ensured non-scalar answers in data processing
audinterface/core/process.py
tests/test_process_text.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ChristianGeng - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Please remove the commented out code blocks - they're no longer needed and make the code harder to read
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

special_args = self._special_args(idx, root, file, process_func_args)
y = self.process_func(data, **special_args, **process_func_args)
# ensure non-scalar answer
y = [y] if len(y) == 1 else y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Handle potential scalar values in _call_data return processing

The len(y) call assumes y is a sequence type. Consider checking if y is a scalar value first to avoid potential AttributeError.

pd.Series: A pandas Series containing the postprocessed data.
"""
ys = [x[0] for x in xs]
# TODO: put into single list comprehension for all these three diagnostics

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Keep separate diagnostic checks for better maintainability

The current separate checks are more readable and easier to debug than a combined list comprehension would be. Consider removing this TODO and keeping the current structure.

all_dict=all(map(lambdax: isinstance(x, dict), [x[0] forxinxs]))
all_iterable=all(map(lambdax: isinstance(x, Iterable), [x[0] forxinxs]))

y = self._postprocess_xs(xs)
return y

@staticmethod

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the type handling and flattening logic into separate helper methods.

The _postprocess_xs method could be simplified while maintaining functionality. Here are two specific suggestions:

  1. Extract the duplicated starts/ends handling into a helper method:
def_flatten_or_collect(items):
"""Flatten iterable items or collect None values."""try:
returnlist(itertools.chain.from_iterable(items))
exceptTypeError:
# Handle case where all items are Noneif [xforxinfilter(None, items)] == []:
returnitemsraisedef_postprocess_xs(xs):
ys= [x[0] forxinxs]
# Simplify type handling with clear conversion rulesifall(isinstance(x, dict) forxinys):
keys=list(itertools.chain.from_iterable(x.keys() forxinys))
values=list(itertools.chain.from_iterable(x.values() forxinys))
y= [{k: v} fork, vinzip(keys, values)]
elifall(isinstance(x, str) forxinys):
y= [[x] forxinys] # Preserve text items as single-item listselse:
y=list(itertools.chain.from_iterable(ys))
files=list(itertools.chain.from_iterable(x[1] forxinxs))
starts=_flatten_or_collect([x[2] forxinxs])
ends=_flatten_or_collect([x[3] forxinxs])
# Rest of the method...
  1. Consider making the type handling more explicit by documenting expected types and using a simpler pattern that handles just the required cases rather than trying to detect all possibilities.

Comment on lines +140 to +141
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +152 to +153
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +509 to 510
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +606 to +607
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +656 to +666
pass
starts_non_iterable = [x for x in filter(None, [x[2] for x in xs])] == []
assert starts_non_iterable, "unknown problem"
starts = [x[2] for x in xs]

# same as for starts
try:
ends = list(itertools.chain.from_iterable([x[3] for x in xs]))
except TypeError:
pass
ends_non_iterable = [x for x in filter(None, [x[3] for x in xs])] == []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:


Explanation
Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.

Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.


def _get_idx_type(preserve_index, segment_is_None, idx):
"""Get expected index type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

Comment on lines +239 to +240
file = idx
yield file, value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

  • Simplify conditional into switch-like form [×2] (switch)
  • Inline variable that is only used once (inline-variable)
Suggested change
file=idx
yieldfile, value
yield (idx, value)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Process text sketch - #180

Open
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch
Open

Process text sketch#180
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch

Conversation

@ChristianGeng

@ChristianGengChristianGeng commented Nov 14, 2024

Copy link
Copy Markdown
Member

Summary by Sourcery

Enhance the processing of text and audio data by introducing a new series generator for index handling and refactoring the post-processing logic into a separate method. Update tests to cover new functionality and improve code maintainability.

Enhancements:

  • Refactor the process index function to use a new series generator for handling different index types.
  • Introduce a new method _postprocess_xs to handle post-processing of processed data, improving code reuse and readability.

Tests:

  • Add new test cases to cover the data_identity function and process_func parameter in the test_process_index function.

@sourcery-ai

sourcery-aiBot commented Nov 14, 2024

Copy link
Copy Markdown
Contributor

Reviewer's Guide by Sourcery

This PR implements improvements to text processing functionality, focusing on handling different types of data and indices in the audio processing pipeline. The changes include better handling of file types, improved index type management, and enhanced data processing capabilities.

Sequence diagram for process_file method

sequenceDiagram
participant Process
participant Utils
Process->>Utils: read_text(file, root)
Note right of Process: For text files
Process->>Utils: read_audio(file, start, end, root)
Note right of Process: For audio/video files
Process->>Process: _process_data(data, idx)
Process->>Process: _process_data(signal, sampling_rate, idx)
Loading

Updated class diagram for Process class

classDiagram
class Process {
+int num_workers
+bool multiprocessing
+bool verbose
+Callable read_func
+void _process_file(String file, ...)
+Series process_files(List files)
+Series process_folder(String folder)
+Series _process_index_wo_segment(Index index)
+static Series _postprocess_xs(List xs)
+Any _call_data(...)
+Dict _special_args(...)
}
note for Process "Added read_func attribute and updated methods to use it"
Loading

File-Level Changes

ChangeDetailsFiles
Added new data processing and index type handling functionality
  • Added helper function to determine index type based on preserve_index and segment flags
  • Implemented series generator function to handle different index types
  • Added support for process_func parameter in test_process_index
  • Enhanced handling of None values in index processing
tests/test_process_text.py
Enhanced Process class with improved file reading capabilities
  • Added read_func parameter to Process class initialization
  • Implemented dynamic setting of read_audio and read_text methods
  • Made sampling_rate parameter optional in identity function
  • Added postprocessing method for handling different data types
audinterface/core/process.py
Improved error handling and edge cases
  • Changed RuntimeError to FileNotFoundError for missing files
  • Added handling for dictionary and iterable text data types
  • Improved handling of None values in starts and ends lists
  • Ensured non-scalar answers in data processing
audinterface/core/process.py
tests/test_process_text.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ChristianGeng - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Please remove the commented out code blocks - they're no longer needed and make the code harder to read
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

special_args = self._special_args(idx, root, file, process_func_args)
y = self.process_func(data, **special_args, **process_func_args)
# ensure non-scalar answer
y = [y] if len(y) == 1 else y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Handle potential scalar values in _call_data return processing

The len(y) call assumes y is a sequence type. Consider checking if y is a scalar value first to avoid potential AttributeError.

pd.Series: A pandas Series containing the postprocessed data.
"""
ys = [x[0] for x in xs]
# TODO: put into single list comprehension for all these three diagnostics

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Keep separate diagnostic checks for better maintainability

The current separate checks are more readable and easier to debug than a combined list comprehension would be. Consider removing this TODO and keeping the current structure.

all_dict=all(map(lambdax: isinstance(x, dict), [x[0] forxinxs]))
all_iterable=all(map(lambdax: isinstance(x, Iterable), [x[0] forxinxs]))

y = self._postprocess_xs(xs)
return y

@staticmethod

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the type handling and flattening logic into separate helper methods.

The _postprocess_xs method could be simplified while maintaining functionality. Here are two specific suggestions:

  1. Extract the duplicated starts/ends handling into a helper method:
def_flatten_or_collect(items):
"""Flatten iterable items or collect None values."""try:
returnlist(itertools.chain.from_iterable(items))
exceptTypeError:
# Handle case where all items are Noneif [xforxinfilter(None, items)] == []:
returnitemsraisedef_postprocess_xs(xs):
ys= [x[0] forxinxs]
# Simplify type handling with clear conversion rulesifall(isinstance(x, dict) forxinys):
keys=list(itertools.chain.from_iterable(x.keys() forxinys))
values=list(itertools.chain.from_iterable(x.values() forxinys))
y= [{k: v} fork, vinzip(keys, values)]
elifall(isinstance(x, str) forxinys):
y= [[x] forxinys] # Preserve text items as single-item listselse:
y=list(itertools.chain.from_iterable(ys))
files=list(itertools.chain.from_iterable(x[1] forxinxs))
starts=_flatten_or_collect([x[2] forxinxs])
ends=_flatten_or_collect([x[3] forxinxs])
# Rest of the method...
  1. Consider making the type handling more explicit by documenting expected types and using a simpler pattern that handles just the required cases rather than trying to detect all possibilities.

Comment on lines +140 to +141
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +152 to +153
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +509 to 510
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +606 to +607
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +656 to +666
pass
starts_non_iterable = [x for x in filter(None, [x[2] for x in xs])] == []
assert starts_non_iterable, "unknown problem"
starts = [x[2] for x in xs]

# same as for starts
try:
ends = list(itertools.chain.from_iterable([x[3] for x in xs]))
except TypeError:
pass
ends_non_iterable = [x for x in filter(None, [x[3] for x in xs])] == []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:


Explanation
Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.

Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.


def _get_idx_type(preserve_index, segment_is_None, idx):
"""Get expected index type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

Comment on lines +239 to +240
file = idx
yield file, value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

  • Simplify conditional into switch-like form [×2] (switch)
  • Inline variable that is only used once (inline-variable)
Suggested change
file=idx
yieldfile, value
yield (idx, value)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Process text sketch - #180

Open
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch
Open

Process text sketch#180
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch

Conversation

@ChristianGeng

@ChristianGengChristianGeng commented Nov 14, 2024

Copy link
Copy Markdown
Member

Summary by Sourcery

Enhance the processing of text and audio data by introducing a new series generator for index handling and refactoring the post-processing logic into a separate method. Update tests to cover new functionality and improve code maintainability.

Enhancements:

  • Refactor the process index function to use a new series generator for handling different index types.
  • Introduce a new method _postprocess_xs to handle post-processing of processed data, improving code reuse and readability.

Tests:

  • Add new test cases to cover the data_identity function and process_func parameter in the test_process_index function.

@sourcery-ai

sourcery-aiBot commented Nov 14, 2024

Copy link
Copy Markdown
Contributor

Reviewer's Guide by Sourcery

This PR implements improvements to text processing functionality, focusing on handling different types of data and indices in the audio processing pipeline. The changes include better handling of file types, improved index type management, and enhanced data processing capabilities.

Sequence diagram for process_file method

sequenceDiagram
participant Process
participant Utils
Process->>Utils: read_text(file, root)
Note right of Process: For text files
Process->>Utils: read_audio(file, start, end, root)
Note right of Process: For audio/video files
Process->>Process: _process_data(data, idx)
Process->>Process: _process_data(signal, sampling_rate, idx)
Loading

Updated class diagram for Process class

classDiagram
class Process {
+int num_workers
+bool multiprocessing
+bool verbose
+Callable read_func
+void _process_file(String file, ...)
+Series process_files(List files)
+Series process_folder(String folder)
+Series _process_index_wo_segment(Index index)
+static Series _postprocess_xs(List xs)
+Any _call_data(...)
+Dict _special_args(...)
}
note for Process "Added read_func attribute and updated methods to use it"
Loading

File-Level Changes

ChangeDetailsFiles
Added new data processing and index type handling functionality
  • Added helper function to determine index type based on preserve_index and segment flags
  • Implemented series generator function to handle different index types
  • Added support for process_func parameter in test_process_index
  • Enhanced handling of None values in index processing
tests/test_process_text.py
Enhanced Process class with improved file reading capabilities
  • Added read_func parameter to Process class initialization
  • Implemented dynamic setting of read_audio and read_text methods
  • Made sampling_rate parameter optional in identity function
  • Added postprocessing method for handling different data types
audinterface/core/process.py
Improved error handling and edge cases
  • Changed RuntimeError to FileNotFoundError for missing files
  • Added handling for dictionary and iterable text data types
  • Improved handling of None values in starts and ends lists
  • Ensured non-scalar answers in data processing
audinterface/core/process.py
tests/test_process_text.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ChristianGeng - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Please remove the commented out code blocks - they're no longer needed and make the code harder to read
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

special_args = self._special_args(idx, root, file, process_func_args)
y = self.process_func(data, **special_args, **process_func_args)
# ensure non-scalar answer
y = [y] if len(y) == 1 else y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Handle potential scalar values in _call_data return processing

The len(y) call assumes y is a sequence type. Consider checking if y is a scalar value first to avoid potential AttributeError.

pd.Series: A pandas Series containing the postprocessed data.
"""
ys = [x[0] for x in xs]
# TODO: put into single list comprehension for all these three diagnostics

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Keep separate diagnostic checks for better maintainability

The current separate checks are more readable and easier to debug than a combined list comprehension would be. Consider removing this TODO and keeping the current structure.

all_dict=all(map(lambdax: isinstance(x, dict), [x[0] forxinxs]))
all_iterable=all(map(lambdax: isinstance(x, Iterable), [x[0] forxinxs]))

y = self._postprocess_xs(xs)
return y

@staticmethod

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the type handling and flattening logic into separate helper methods.

The _postprocess_xs method could be simplified while maintaining functionality. Here are two specific suggestions:

  1. Extract the duplicated starts/ends handling into a helper method:
def_flatten_or_collect(items):
"""Flatten iterable items or collect None values."""try:
returnlist(itertools.chain.from_iterable(items))
exceptTypeError:
# Handle case where all items are Noneif [xforxinfilter(None, items)] == []:
returnitemsraisedef_postprocess_xs(xs):
ys= [x[0] forxinxs]
# Simplify type handling with clear conversion rulesifall(isinstance(x, dict) forxinys):
keys=list(itertools.chain.from_iterable(x.keys() forxinys))
values=list(itertools.chain.from_iterable(x.values() forxinys))
y= [{k: v} fork, vinzip(keys, values)]
elifall(isinstance(x, str) forxinys):
y= [[x] forxinys] # Preserve text items as single-item listselse:
y=list(itertools.chain.from_iterable(ys))
files=list(itertools.chain.from_iterable(x[1] forxinxs))
starts=_flatten_or_collect([x[2] forxinxs])
ends=_flatten_or_collect([x[3] forxinxs])
# Rest of the method...
  1. Consider making the type handling more explicit by documenting expected types and using a simpler pattern that handles just the required cases rather than trying to detect all possibilities.

Comment on lines +140 to +141
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +152 to +153
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +509 to 510
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +606 to +607
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +656 to +666
pass
starts_non_iterable = [x for x in filter(None, [x[2] for x in xs])] == []
assert starts_non_iterable, "unknown problem"
starts = [x[2] for x in xs]

# same as for starts
try:
ends = list(itertools.chain.from_iterable([x[3] for x in xs]))
except TypeError:
pass
ends_non_iterable = [x for x in filter(None, [x[3] for x in xs])] == []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:


Explanation
Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.

Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.


def _get_idx_type(preserve_index, segment_is_None, idx):
"""Get expected index type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

Comment on lines +239 to +240
file = idx
yield file, value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

  • Simplify conditional into switch-like form [×2] (switch)
  • Inline variable that is only used once (inline-variable)
Suggested change
file=idx
yieldfile, value
yield (idx, value)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Process text sketch - #180

Open
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch
Open

Process text sketch#180
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch

Conversation

@ChristianGeng

@ChristianGengChristianGeng commented Nov 14, 2024

Copy link
Copy Markdown
Member

Summary by Sourcery

Enhance the processing of text and audio data by introducing a new series generator for index handling and refactoring the post-processing logic into a separate method. Update tests to cover new functionality and improve code maintainability.

Enhancements:

  • Refactor the process index function to use a new series generator for handling different index types.
  • Introduce a new method _postprocess_xs to handle post-processing of processed data, improving code reuse and readability.

Tests:

  • Add new test cases to cover the data_identity function and process_func parameter in the test_process_index function.

@sourcery-ai

sourcery-aiBot commented Nov 14, 2024

Copy link
Copy Markdown
Contributor

Reviewer's Guide by Sourcery

This PR implements improvements to text processing functionality, focusing on handling different types of data and indices in the audio processing pipeline. The changes include better handling of file types, improved index type management, and enhanced data processing capabilities.

Sequence diagram for process_file method

sequenceDiagram
participant Process
participant Utils
Process->>Utils: read_text(file, root)
Note right of Process: For text files
Process->>Utils: read_audio(file, start, end, root)
Note right of Process: For audio/video files
Process->>Process: _process_data(data, idx)
Process->>Process: _process_data(signal, sampling_rate, idx)
Loading

Updated class diagram for Process class

classDiagram
class Process {
+int num_workers
+bool multiprocessing
+bool verbose
+Callable read_func
+void _process_file(String file, ...)
+Series process_files(List files)
+Series process_folder(String folder)
+Series _process_index_wo_segment(Index index)
+static Series _postprocess_xs(List xs)
+Any _call_data(...)
+Dict _special_args(...)
}
note for Process "Added read_func attribute and updated methods to use it"
Loading

File-Level Changes

ChangeDetailsFiles
Added new data processing and index type handling functionality
  • Added helper function to determine index type based on preserve_index and segment flags
  • Implemented series generator function to handle different index types
  • Added support for process_func parameter in test_process_index
  • Enhanced handling of None values in index processing
tests/test_process_text.py
Enhanced Process class with improved file reading capabilities
  • Added read_func parameter to Process class initialization
  • Implemented dynamic setting of read_audio and read_text methods
  • Made sampling_rate parameter optional in identity function
  • Added postprocessing method for handling different data types
audinterface/core/process.py
Improved error handling and edge cases
  • Changed RuntimeError to FileNotFoundError for missing files
  • Added handling for dictionary and iterable text data types
  • Improved handling of None values in starts and ends lists
  • Ensured non-scalar answers in data processing
audinterface/core/process.py
tests/test_process_text.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ChristianGeng - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Please remove the commented out code blocks - they're no longer needed and make the code harder to read
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

special_args = self._special_args(idx, root, file, process_func_args)
y = self.process_func(data, **special_args, **process_func_args)
# ensure non-scalar answer
y = [y] if len(y) == 1 else y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Handle potential scalar values in _call_data return processing

The len(y) call assumes y is a sequence type. Consider checking if y is a scalar value first to avoid potential AttributeError.

pd.Series: A pandas Series containing the postprocessed data.
"""
ys = [x[0] for x in xs]
# TODO: put into single list comprehension for all these three diagnostics

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Keep separate diagnostic checks for better maintainability

The current separate checks are more readable and easier to debug than a combined list comprehension would be. Consider removing this TODO and keeping the current structure.

all_dict=all(map(lambdax: isinstance(x, dict), [x[0] forxinxs]))
all_iterable=all(map(lambdax: isinstance(x, Iterable), [x[0] forxinxs]))

y = self._postprocess_xs(xs)
return y

@staticmethod

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the type handling and flattening logic into separate helper methods.

The _postprocess_xs method could be simplified while maintaining functionality. Here are two specific suggestions:

  1. Extract the duplicated starts/ends handling into a helper method:
def_flatten_or_collect(items):
"""Flatten iterable items or collect None values."""try:
returnlist(itertools.chain.from_iterable(items))
exceptTypeError:
# Handle case where all items are Noneif [xforxinfilter(None, items)] == []:
returnitemsraisedef_postprocess_xs(xs):
ys= [x[0] forxinxs]
# Simplify type handling with clear conversion rulesifall(isinstance(x, dict) forxinys):
keys=list(itertools.chain.from_iterable(x.keys() forxinys))
values=list(itertools.chain.from_iterable(x.values() forxinys))
y= [{k: v} fork, vinzip(keys, values)]
elifall(isinstance(x, str) forxinys):
y= [[x] forxinys] # Preserve text items as single-item listselse:
y=list(itertools.chain.from_iterable(ys))
files=list(itertools.chain.from_iterable(x[1] forxinxs))
starts=_flatten_or_collect([x[2] forxinxs])
ends=_flatten_or_collect([x[3] forxinxs])
# Rest of the method...
  1. Consider making the type handling more explicit by documenting expected types and using a simpler pattern that handles just the required cases rather than trying to detect all possibilities.

Comment on lines +140 to +141
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +152 to +153
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +509 to 510
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +606 to +607
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +656 to +666
pass
starts_non_iterable = [x for x in filter(None, [x[2] for x in xs])] == []
assert starts_non_iterable, "unknown problem"
starts = [x[2] for x in xs]

# same as for starts
try:
ends = list(itertools.chain.from_iterable([x[3] for x in xs]))
except TypeError:
pass
ends_non_iterable = [x for x in filter(None, [x[3] for x in xs])] == []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:


Explanation
Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.

Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.


def _get_idx_type(preserve_index, segment_is_None, idx):
"""Get expected index type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

Comment on lines +239 to +240
file = idx
yield file, value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

  • Simplify conditional into switch-like form [×2] (switch)
  • Inline variable that is only used once (inline-variable)
Suggested change
file=idx
yieldfile, value
yield (idx, value)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Process text sketch - #180

Open
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch
Open

Process text sketch#180
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch

Conversation

@ChristianGeng

@ChristianGengChristianGeng commented Nov 14, 2024

Copy link
Copy Markdown
Member

Summary by Sourcery

Enhance the processing of text and audio data by introducing a new series generator for index handling and refactoring the post-processing logic into a separate method. Update tests to cover new functionality and improve code maintainability.

Enhancements:

  • Refactor the process index function to use a new series generator for handling different index types.
  • Introduce a new method _postprocess_xs to handle post-processing of processed data, improving code reuse and readability.

Tests:

  • Add new test cases to cover the data_identity function and process_func parameter in the test_process_index function.

@sourcery-ai

sourcery-aiBot commented Nov 14, 2024

Copy link
Copy Markdown
Contributor

Reviewer's Guide by Sourcery

This PR implements improvements to text processing functionality, focusing on handling different types of data and indices in the audio processing pipeline. The changes include better handling of file types, improved index type management, and enhanced data processing capabilities.

Sequence diagram for process_file method

sequenceDiagram
participant Process
participant Utils
Process->>Utils: read_text(file, root)
Note right of Process: For text files
Process->>Utils: read_audio(file, start, end, root)
Note right of Process: For audio/video files
Process->>Process: _process_data(data, idx)
Process->>Process: _process_data(signal, sampling_rate, idx)
Loading

Updated class diagram for Process class

classDiagram
class Process {
+int num_workers
+bool multiprocessing
+bool verbose
+Callable read_func
+void _process_file(String file, ...)
+Series process_files(List files)
+Series process_folder(String folder)
+Series _process_index_wo_segment(Index index)
+static Series _postprocess_xs(List xs)
+Any _call_data(...)
+Dict _special_args(...)
}
note for Process "Added read_func attribute and updated methods to use it"
Loading

File-Level Changes

ChangeDetailsFiles
Added new data processing and index type handling functionality
  • Added helper function to determine index type based on preserve_index and segment flags
  • Implemented series generator function to handle different index types
  • Added support for process_func parameter in test_process_index
  • Enhanced handling of None values in index processing
tests/test_process_text.py
Enhanced Process class with improved file reading capabilities
  • Added read_func parameter to Process class initialization
  • Implemented dynamic setting of read_audio and read_text methods
  • Made sampling_rate parameter optional in identity function
  • Added postprocessing method for handling different data types
audinterface/core/process.py
Improved error handling and edge cases
  • Changed RuntimeError to FileNotFoundError for missing files
  • Added handling for dictionary and iterable text data types
  • Improved handling of None values in starts and ends lists
  • Ensured non-scalar answers in data processing
audinterface/core/process.py
tests/test_process_text.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ChristianGeng - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Please remove the commented out code blocks - they're no longer needed and make the code harder to read
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

special_args = self._special_args(idx, root, file, process_func_args)
y = self.process_func(data, **special_args, **process_func_args)
# ensure non-scalar answer
y = [y] if len(y) == 1 else y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Handle potential scalar values in _call_data return processing

The len(y) call assumes y is a sequence type. Consider checking if y is a scalar value first to avoid potential AttributeError.

pd.Series: A pandas Series containing the postprocessed data.
"""
ys = [x[0] for x in xs]
# TODO: put into single list comprehension for all these three diagnostics

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Keep separate diagnostic checks for better maintainability

The current separate checks are more readable and easier to debug than a combined list comprehension would be. Consider removing this TODO and keeping the current structure.

all_dict=all(map(lambdax: isinstance(x, dict), [x[0] forxinxs]))
all_iterable=all(map(lambdax: isinstance(x, Iterable), [x[0] forxinxs]))

y = self._postprocess_xs(xs)
return y

@staticmethod

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the type handling and flattening logic into separate helper methods.

The _postprocess_xs method could be simplified while maintaining functionality. Here are two specific suggestions:

  1. Extract the duplicated starts/ends handling into a helper method:
def_flatten_or_collect(items):
"""Flatten iterable items or collect None values."""try:
returnlist(itertools.chain.from_iterable(items))
exceptTypeError:
# Handle case where all items are Noneif [xforxinfilter(None, items)] == []:
returnitemsraisedef_postprocess_xs(xs):
ys= [x[0] forxinxs]
# Simplify type handling with clear conversion rulesifall(isinstance(x, dict) forxinys):
keys=list(itertools.chain.from_iterable(x.keys() forxinys))
values=list(itertools.chain.from_iterable(x.values() forxinys))
y= [{k: v} fork, vinzip(keys, values)]
elifall(isinstance(x, str) forxinys):
y= [[x] forxinys] # Preserve text items as single-item listselse:
y=list(itertools.chain.from_iterable(ys))
files=list(itertools.chain.from_iterable(x[1] forxinxs))
starts=_flatten_or_collect([x[2] forxinxs])
ends=_flatten_or_collect([x[3] forxinxs])
# Rest of the method...
  1. Consider making the type handling more explicit by documenting expected types and using a simpler pattern that handles just the required cases rather than trying to detect all possibilities.

Comment on lines +140 to +141
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +152 to +153
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +509 to 510
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +606 to +607
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +656 to +666
pass
starts_non_iterable = [x for x in filter(None, [x[2] for x in xs])] == []
assert starts_non_iterable, "unknown problem"
starts = [x[2] for x in xs]

# same as for starts
try:
ends = list(itertools.chain.from_iterable([x[3] for x in xs]))
except TypeError:
pass
ends_non_iterable = [x for x in filter(None, [x[3] for x in xs])] == []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:


Explanation
Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.

Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.


def _get_idx_type(preserve_index, segment_is_None, idx):
"""Get expected index type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

Comment on lines +239 to +240
file = idx
yield file, value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

  • Simplify conditional into switch-like form [×2] (switch)
  • Inline variable that is only used once (inline-variable)
Suggested change
file=idx
yieldfile, value
yield (idx, value)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Process text sketch - #180

Open
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch
Open

Process text sketch#180
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch

Conversation

@ChristianGeng

@ChristianGengChristianGeng commented Nov 14, 2024

Copy link
Copy Markdown
Member

Summary by Sourcery

Enhance the processing of text and audio data by introducing a new series generator for index handling and refactoring the post-processing logic into a separate method. Update tests to cover new functionality and improve code maintainability.

Enhancements:

  • Refactor the process index function to use a new series generator for handling different index types.
  • Introduce a new method _postprocess_xs to handle post-processing of processed data, improving code reuse and readability.

Tests:

  • Add new test cases to cover the data_identity function and process_func parameter in the test_process_index function.

@sourcery-ai

sourcery-aiBot commented Nov 14, 2024

Copy link
Copy Markdown
Contributor

Reviewer's Guide by Sourcery

This PR implements improvements to text processing functionality, focusing on handling different types of data and indices in the audio processing pipeline. The changes include better handling of file types, improved index type management, and enhanced data processing capabilities.

Sequence diagram for process_file method

sequenceDiagram
participant Process
participant Utils
Process->>Utils: read_text(file, root)
Note right of Process: For text files
Process->>Utils: read_audio(file, start, end, root)
Note right of Process: For audio/video files
Process->>Process: _process_data(data, idx)
Process->>Process: _process_data(signal, sampling_rate, idx)
Loading

Updated class diagram for Process class

classDiagram
class Process {
+int num_workers
+bool multiprocessing
+bool verbose
+Callable read_func
+void _process_file(String file, ...)
+Series process_files(List files)
+Series process_folder(String folder)
+Series _process_index_wo_segment(Index index)
+static Series _postprocess_xs(List xs)
+Any _call_data(...)
+Dict _special_args(...)
}
note for Process "Added read_func attribute and updated methods to use it"
Loading

File-Level Changes

ChangeDetailsFiles
Added new data processing and index type handling functionality
  • Added helper function to determine index type based on preserve_index and segment flags
  • Implemented series generator function to handle different index types
  • Added support for process_func parameter in test_process_index
  • Enhanced handling of None values in index processing
tests/test_process_text.py
Enhanced Process class with improved file reading capabilities
  • Added read_func parameter to Process class initialization
  • Implemented dynamic setting of read_audio and read_text methods
  • Made sampling_rate parameter optional in identity function
  • Added postprocessing method for handling different data types
audinterface/core/process.py
Improved error handling and edge cases
  • Changed RuntimeError to FileNotFoundError for missing files
  • Added handling for dictionary and iterable text data types
  • Improved handling of None values in starts and ends lists
  • Ensured non-scalar answers in data processing
audinterface/core/process.py
tests/test_process_text.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ChristianGeng - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Please remove the commented out code blocks - they're no longer needed and make the code harder to read
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

special_args = self._special_args(idx, root, file, process_func_args)
y = self.process_func(data, **special_args, **process_func_args)
# ensure non-scalar answer
y = [y] if len(y) == 1 else y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Handle potential scalar values in _call_data return processing

The len(y) call assumes y is a sequence type. Consider checking if y is a scalar value first to avoid potential AttributeError.

pd.Series: A pandas Series containing the postprocessed data.
"""
ys = [x[0] for x in xs]
# TODO: put into single list comprehension for all these three diagnostics

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Keep separate diagnostic checks for better maintainability

The current separate checks are more readable and easier to debug than a combined list comprehension would be. Consider removing this TODO and keeping the current structure.

all_dict=all(map(lambdax: isinstance(x, dict), [x[0] forxinxs]))
all_iterable=all(map(lambdax: isinstance(x, Iterable), [x[0] forxinxs]))

y = self._postprocess_xs(xs)
return y

@staticmethod

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the type handling and flattening logic into separate helper methods.

The _postprocess_xs method could be simplified while maintaining functionality. Here are two specific suggestions:

  1. Extract the duplicated starts/ends handling into a helper method:
def_flatten_or_collect(items):
"""Flatten iterable items or collect None values."""try:
returnlist(itertools.chain.from_iterable(items))
exceptTypeError:
# Handle case where all items are Noneif [xforxinfilter(None, items)] == []:
returnitemsraisedef_postprocess_xs(xs):
ys= [x[0] forxinxs]
# Simplify type handling with clear conversion rulesifall(isinstance(x, dict) forxinys):
keys=list(itertools.chain.from_iterable(x.keys() forxinys))
values=list(itertools.chain.from_iterable(x.values() forxinys))
y= [{k: v} fork, vinzip(keys, values)]
elifall(isinstance(x, str) forxinys):
y= [[x] forxinys] # Preserve text items as single-item listselse:
y=list(itertools.chain.from_iterable(ys))
files=list(itertools.chain.from_iterable(x[1] forxinxs))
starts=_flatten_or_collect([x[2] forxinxs])
ends=_flatten_or_collect([x[3] forxinxs])
# Rest of the method...
  1. Consider making the type handling more explicit by documenting expected types and using a simpler pattern that handles just the required cases rather than trying to detect all possibilities.

Comment on lines +140 to +141
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +152 to +153
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +509 to 510
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +606 to +607
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +656 to +666
pass
starts_non_iterable = [x for x in filter(None, [x[2] for x in xs])] == []
assert starts_non_iterable, "unknown problem"
starts = [x[2] for x in xs]

# same as for starts
try:
ends = list(itertools.chain.from_iterable([x[3] for x in xs]))
except TypeError:
pass
ends_non_iterable = [x for x in filter(None, [x[3] for x in xs])] == []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:


Explanation
Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.

Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.


def _get_idx_type(preserve_index, segment_is_None, idx):
"""Get expected index type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

Comment on lines +239 to +240
file = idx
yield file, value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

  • Simplify conditional into switch-like form [×2] (switch)
  • Inline variable that is only used once (inline-variable)
Suggested change
file=idx
yieldfile, value
yield (idx, value)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Process text sketch - #180

Open
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch
Open

Process text sketch#180
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch

Conversation

@ChristianGeng

@ChristianGengChristianGeng commented Nov 14, 2024

Copy link
Copy Markdown
Member

Summary by Sourcery

Enhance the processing of text and audio data by introducing a new series generator for index handling and refactoring the post-processing logic into a separate method. Update tests to cover new functionality and improve code maintainability.

Enhancements:

  • Refactor the process index function to use a new series generator for handling different index types.
  • Introduce a new method _postprocess_xs to handle post-processing of processed data, improving code reuse and readability.

Tests:

  • Add new test cases to cover the data_identity function and process_func parameter in the test_process_index function.

@sourcery-ai

sourcery-aiBot commented Nov 14, 2024

Copy link
Copy Markdown
Contributor

Reviewer's Guide by Sourcery

This PR implements improvements to text processing functionality, focusing on handling different types of data and indices in the audio processing pipeline. The changes include better handling of file types, improved index type management, and enhanced data processing capabilities.

Sequence diagram for process_file method

sequenceDiagram
participant Process
participant Utils
Process->>Utils: read_text(file, root)
Note right of Process: For text files
Process->>Utils: read_audio(file, start, end, root)
Note right of Process: For audio/video files
Process->>Process: _process_data(data, idx)
Process->>Process: _process_data(signal, sampling_rate, idx)
Loading

Updated class diagram for Process class

classDiagram
class Process {
+int num_workers
+bool multiprocessing
+bool verbose
+Callable read_func
+void _process_file(String file, ...)
+Series process_files(List files)
+Series process_folder(String folder)
+Series _process_index_wo_segment(Index index)
+static Series _postprocess_xs(List xs)
+Any _call_data(...)
+Dict _special_args(...)
}
note for Process "Added read_func attribute and updated methods to use it"
Loading

File-Level Changes

ChangeDetailsFiles
Added new data processing and index type handling functionality
  • Added helper function to determine index type based on preserve_index and segment flags
  • Implemented series generator function to handle different index types
  • Added support for process_func parameter in test_process_index
  • Enhanced handling of None values in index processing
tests/test_process_text.py
Enhanced Process class with improved file reading capabilities
  • Added read_func parameter to Process class initialization
  • Implemented dynamic setting of read_audio and read_text methods
  • Made sampling_rate parameter optional in identity function
  • Added postprocessing method for handling different data types
audinterface/core/process.py
Improved error handling and edge cases
  • Changed RuntimeError to FileNotFoundError for missing files
  • Added handling for dictionary and iterable text data types
  • Improved handling of None values in starts and ends lists
  • Ensured non-scalar answers in data processing
audinterface/core/process.py
tests/test_process_text.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ChristianGeng - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Please remove the commented out code blocks - they're no longer needed and make the code harder to read
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

special_args = self._special_args(idx, root, file, process_func_args)
y = self.process_func(data, **special_args, **process_func_args)
# ensure non-scalar answer
y = [y] if len(y) == 1 else y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Handle potential scalar values in _call_data return processing

The len(y) call assumes y is a sequence type. Consider checking if y is a scalar value first to avoid potential AttributeError.

pd.Series: A pandas Series containing the postprocessed data.
"""
ys = [x[0] for x in xs]
# TODO: put into single list comprehension for all these three diagnostics

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Keep separate diagnostic checks for better maintainability

The current separate checks are more readable and easier to debug than a combined list comprehension would be. Consider removing this TODO and keeping the current structure.

all_dict=all(map(lambdax: isinstance(x, dict), [x[0] forxinxs]))
all_iterable=all(map(lambdax: isinstance(x, Iterable), [x[0] forxinxs]))

y = self._postprocess_xs(xs)
return y

@staticmethod

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the type handling and flattening logic into separate helper methods.

The _postprocess_xs method could be simplified while maintaining functionality. Here are two specific suggestions:

  1. Extract the duplicated starts/ends handling into a helper method:
def_flatten_or_collect(items):
"""Flatten iterable items or collect None values."""try:
returnlist(itertools.chain.from_iterable(items))
exceptTypeError:
# Handle case where all items are Noneif [xforxinfilter(None, items)] == []:
returnitemsraisedef_postprocess_xs(xs):
ys= [x[0] forxinxs]
# Simplify type handling with clear conversion rulesifall(isinstance(x, dict) forxinys):
keys=list(itertools.chain.from_iterable(x.keys() forxinys))
values=list(itertools.chain.from_iterable(x.values() forxinys))
y= [{k: v} fork, vinzip(keys, values)]
elifall(isinstance(x, str) forxinys):
y= [[x] forxinys] # Preserve text items as single-item listselse:
y=list(itertools.chain.from_iterable(ys))
files=list(itertools.chain.from_iterable(x[1] forxinxs))
starts=_flatten_or_collect([x[2] forxinxs])
ends=_flatten_or_collect([x[3] forxinxs])
# Rest of the method...
  1. Consider making the type handling more explicit by documenting expected types and using a simpler pattern that handles just the required cases rather than trying to detect all possibilities.

Comment on lines +140 to +141
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +152 to +153
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +509 to 510
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +606 to +607
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +656 to +666
pass
starts_non_iterable = [x for x in filter(None, [x[2] for x in xs])] == []
assert starts_non_iterable, "unknown problem"
starts = [x[2] for x in xs]

# same as for starts
try:
ends = list(itertools.chain.from_iterable([x[3] for x in xs]))
except TypeError:
pass
ends_non_iterable = [x for x in filter(None, [x[3] for x in xs])] == []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:


Explanation
Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.

Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.


def _get_idx_type(preserve_index, segment_is_None, idx):
"""Get expected index type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

Comment on lines +239 to +240
file = idx
yield file, value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

  • Simplify conditional into switch-like form [×2] (switch)
  • Inline variable that is only used once (inline-variable)
Suggested change
file=idx
yieldfile, value
yield (idx, value)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@ChristianGeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Process text sketch - #180

Open
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch
Open

Process text sketch#180
ChristianGeng wants to merge 11 commits into
process-textfrom
process-text-sketch

Conversation

@ChristianGeng

@ChristianGengChristianGeng commented Nov 14, 2024

Copy link
Copy Markdown
Member

Summary by Sourcery

Enhance the processing of text and audio data by introducing a new series generator for index handling and refactoring the post-processing logic into a separate method. Update tests to cover new functionality and improve code maintainability.

Enhancements:

  • Refactor the process index function to use a new series generator for handling different index types.
  • Introduce a new method _postprocess_xs to handle post-processing of processed data, improving code reuse and readability.

Tests:

  • Add new test cases to cover the data_identity function and process_func parameter in the test_process_index function.

@sourcery-ai

sourcery-aiBot commented Nov 14, 2024

Copy link
Copy Markdown
Contributor

Reviewer's Guide by Sourcery

This PR implements improvements to text processing functionality, focusing on handling different types of data and indices in the audio processing pipeline. The changes include better handling of file types, improved index type management, and enhanced data processing capabilities.

Sequence diagram for process_file method

sequenceDiagram
participant Process
participant Utils
Process->>Utils: read_text(file, root)
Note right of Process: For text files
Process->>Utils: read_audio(file, start, end, root)
Note right of Process: For audio/video files
Process->>Process: _process_data(data, idx)
Process->>Process: _process_data(signal, sampling_rate, idx)
Loading

Updated class diagram for Process class

classDiagram
class Process {
+int num_workers
+bool multiprocessing
+bool verbose
+Callable read_func
+void _process_file(String file, ...)
+Series process_files(List files)
+Series process_folder(String folder)
+Series _process_index_wo_segment(Index index)
+static Series _postprocess_xs(List xs)
+Any _call_data(...)
+Dict _special_args(...)
}
note for Process "Added read_func attribute and updated methods to use it"
Loading

File-Level Changes

ChangeDetailsFiles
Added new data processing and index type handling functionality
  • Added helper function to determine index type based on preserve_index and segment flags
  • Implemented series generator function to handle different index types
  • Added support for process_func parameter in test_process_index
  • Enhanced handling of None values in index processing
tests/test_process_text.py
Enhanced Process class with improved file reading capabilities
  • Added read_func parameter to Process class initialization
  • Implemented dynamic setting of read_audio and read_text methods
  • Made sampling_rate parameter optional in identity function
  • Added postprocessing method for handling different data types
audinterface/core/process.py
Improved error handling and edge cases
  • Changed RuntimeError to FileNotFoundError for missing files
  • Added handling for dictionary and iterable text data types
  • Improved handling of None values in starts and ends lists
  • Ensured non-scalar answers in data processing
audinterface/core/process.py
tests/test_process_text.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-aisourcery-aiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ChristianGeng - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Please remove the commented out code blocks - they're no longer needed and make the code harder to read
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

special_args = self._special_args(idx, root, file, process_func_args)
y = self.process_func(data, **special_args, **process_func_args)
# ensure non-scalar answer
y = [y] if len(y) == 1 else y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Handle potential scalar values in _call_data return processing

The len(y) call assumes y is a sequence type. Consider checking if y is a scalar value first to avoid potential AttributeError.

pd.Series: A pandas Series containing the postprocessed data.
"""
ys = [x[0] for x in xs]
# TODO: put into single list comprehension for all these three diagnostics

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Keep separate diagnostic checks for better maintainability

The current separate checks are more readable and easier to debug than a combined list comprehension would be. Consider removing this TODO and keeping the current structure.

all_dict=all(map(lambdax: isinstance(x, dict), [x[0] forxinxs]))
all_iterable=all(map(lambdax: isinstance(x, Iterable), [x[0] forxinxs]))

y = self._postprocess_xs(xs)
return y

@staticmethod

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the type handling and flattening logic into separate helper methods.

The _postprocess_xs method could be simplified while maintaining functionality. Here are two specific suggestions:

  1. Extract the duplicated starts/ends handling into a helper method:
def_flatten_or_collect(items):
"""Flatten iterable items or collect None values."""try:
returnlist(itertools.chain.from_iterable(items))
exceptTypeError:
# Handle case where all items are Noneif [xforxinfilter(None, items)] == []:
returnitemsraisedef_postprocess_xs(xs):
ys= [x[0] forxinxs]
# Simplify type handling with clear conversion rulesifall(isinstance(x, dict) forxinys):
keys=list(itertools.chain.from_iterable(x.keys() forxinys))
values=list(itertools.chain.from_iterable(x.values() forxinys))
y= [{k: v} fork, vinzip(keys, values)]
elifall(isinstance(x, str) forxinys):
y= [[x] forxinys] # Preserve text items as single-item listselse:
y=list(itertools.chain.from_iterable(ys))
files=list(itertools.chain.from_iterable(x[1] forxinxs))
starts=_flatten_or_collect([x[2] forxinxs])
ends=_flatten_or_collect([x[3] forxinxs])
# Rest of the method...
  1. Consider making the type handling more explicit by documenting expected types and using a simpler pattern that handles just the required cases rather than trying to detect all possibilities.

Comment on lines +140 to +141
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +152 to +153
if num_files == 0:
index = pd.RangeIndex(0, 0, 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines +509 to 510
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +606 to +607
y = self._postprocess_xs(xs)
return y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
y=self._postprocess_xs(xs)
returny
returnself._postprocess_xs(xs)

Comment on lines +656 to +666
pass
starts_non_iterable = [x for x in filter(None, [x[2] for x in xs])] == []
assert starts_non_iterable, "unknown problem"
starts = [x[2] for x in xs]

# same as for starts
try:
ends = list(itertools.chain.from_iterable([x[3] for x in xs]))
except TypeError:
pass
ends_non_iterable = [x for x in filter(None, [x[3] for x in xs])] == []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:


Explanation
Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.

Convert list/set/tuple comprehensions that do not change the input elements into.

Before

# List comprehensions
[itemforitemincoll]
[itemforiteminfriends.names()]
# Dict comprehensions
{k: vfork, vincoll}
{k: vfork, vincoll.items()} # Only if we know coll is a `dict`# Unneeded call to `.items()`dict(coll.items()) # Only if we know coll is a `dict`# Set comprehensions
{itemforitemincoll}

After

# List comprehensionslist(iter(coll))
list(iter(friends.names()))
# Dict comprehensionsdict(coll)
dict(coll)
# Unneeded call to `.items()`dict(coll)
# Set comprehensionsset(coll)

All these comprehensions are just creating a copy of the original collection.
They can all be simplified by simply constructing a new collection directly. The
resulting code is easier to read and shows the intent more clearly.


def _get_idx_type(preserve_index, segment_is_None, idx):
"""Get expected index type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

Comment on lines +239 to +240
file = idx
yield file, value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

  • Simplify conditional into switch-like form [×2] (switch)
  • Inline variable that is only used once (inline-variable)
Suggested change
file=idx
yieldfile, value
yield (idx, value)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@ChristianGeng