diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 4aacf61112336fd3d0a23ea3485a7f1650dd1b37..16862992b5672e4b5235584bb6ac23cf22f7e0e6 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -1490,6 +1490,12 @@ changes: ["vendor/gems/mail-smtp_pool/**/*"] - <<: *if-merge-request-labels-run-all-rspec +.vendor:rules:ipynbdiff: + rules: + - <<: *if-merge-request + changes: ["vendor/gems/ipynbdiff/**/*"] + - <<: *if-merge-request-labels-run-all-rspec + ################## # Releases rules # ################## diff --git a/.gitlab/ci/vendored-gems.gitlab-ci.yml b/.gitlab/ci/vendored-gems.gitlab-ci.yml index a39c4307c13e095a9673b6a0eb370e98f2f8bf48..ce71820100f991fb7731708b6e3a834b24fc66d3 100644 --- a/.gitlab/ci/vendored-gems.gitlab-ci.yml +++ b/.gitlab/ci/vendored-gems.gitlab-ci.yml @@ -5,3 +5,10 @@ vendor mail-smtp_pool: trigger: include: vendor/gems/mail-smtp_pool/.gitlab-ci.yml strategy: depend +vendor ipynbdiff: + extends: + - .vendor:rules:ipynbdiff + needs: [] + trigger: + include: vendor/gems/ipynbdiff/.gitlab-ci.yml + strategy: depend diff --git a/Gemfile b/Gemfile index c554d509d0efb04ee3a32145b32c46347618c902..4e320d223e244bf23fb390dde2d9061d3a4fa962 100644 --- a/Gemfile +++ b/Gemfile @@ -546,6 +546,6 @@ gem 'ipaddress', '~> 0.8.3' gem 'parslet', '~> 1.8' -gem 'ipynbdiff', '0.4.7' +gem 'ipynbdiff', path: 'vendor/gems/ipynbdiff' gem 'ed25519', '~> 1.3.0' diff --git a/Gemfile.lock b/Gemfile.lock index dd2b7196115a32a38599a0b1c685fdea292393dd..7c84d4842506912df9cfc6e2a7ebeaabcf196508 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,10 @@ +PATH + remote: vendor/gems/ipynbdiff + specs: + ipynbdiff (0.4.7) + diffy (~> 3.3) + json (~> 2.5, >= 2.5.1) + PATH remote: vendor/gems/mail-smtp_pool specs: @@ -667,9 +674,6 @@ GEM invisible_captcha (1.1.0) rails (>= 4.2) ipaddress (0.8.3) - ipynbdiff (0.4.7) - diffy (~> 3.3) - json (~> 2.5, >= 2.5.1) jaeger-client (1.1.0) opentracing (~> 0.3) thrift @@ -1575,7 +1579,7 @@ DEPENDENCIES icalendar invisible_captcha (~> 1.1.0) ipaddress (~> 0.8.3) - ipynbdiff (= 0.4.7) + ipynbdiff! jira-ruby (~> 2.1.4) js_regex (~> 3.7) json (~> 2.5.1) diff --git a/vendor/gems/ipynbdiff/.gitignore b/vendor/gems/ipynbdiff/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4f284c35a42069bc139e86aa9edc504e0978ebdb --- /dev/null +++ b/vendor/gems/ipynbdiff/.gitignore @@ -0,0 +1,2 @@ +*.gem +.bundle diff --git a/vendor/gems/ipynbdiff/.gitlab-ci.yml b/vendor/gems/ipynbdiff/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..7b0c9df6cd9fde227031dba7a331c5d29f430334 --- /dev/null +++ b/vendor/gems/ipynbdiff/.gitlab-ci.yml @@ -0,0 +1,32 @@ +# You can override the included template(s) by including variable overrides +# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings +# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings +# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings +# Note that environment variables can be set in several places +# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence +workflow: + rules: + - if: $CI_MERGE_REQUEST_ID + +.rspec: + cache: + key: ipynbdiff + paths: + - vendor/gems/ipynbdiff/vendor/ruby + before_script: + - cd vendor/gems/ipynbdiff + - ruby -v # Print out ruby version for debugging + - gem install bundler --no-document # Bundler is not installed with the image + - bundle config set --local path 'vendor' # Install dependencies into ./vendor/ruby + - bundle config set with 'development' + - bundle install -j $(nproc) + script: + - bundle exec rspec + +rspec-2.7: + image: "ruby:2.7" + extends: .rspec + +rspec-3.0: + image: "ruby:3.0" + extends: .rspec diff --git a/vendor/gems/ipynbdiff/Gemfile b/vendor/gems/ipynbdiff/Gemfile new file mode 100644 index 0000000000000000000000000000000000000000..7f4f5e950d1572e1ce5607ca237375aa2e30d662 --- /dev/null +++ b/vendor/gems/ipynbdiff/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +gemspec diff --git a/vendor/gems/ipynbdiff/Gemfile.lock b/vendor/gems/ipynbdiff/Gemfile.lock new file mode 100644 index 0000000000000000000000000000000000000000..a5e8e3e4e86bb90231e5dd2c2d2e46f074de5643 --- /dev/null +++ b/vendor/gems/ipynbdiff/Gemfile.lock @@ -0,0 +1,64 @@ +PATH + remote: . + specs: + ipynbdiff (0.4.7) + diffy (~> 3.3) + json (~> 2.5, >= 2.5.1) + +GEM + remote: https://rubygems.org/ + specs: + ast (2.4.2) + binding_ninja (0.2.3) + coderay (1.1.3) + diff-lcs (1.5.0) + diffy (3.4.2) + json (2.6.2) + method_source (1.0.0) + parser (3.1.2.0) + ast (~> 2.4.1) + proc_to_ast (0.1.0) + coderay + parser + unparser + pry (0.14.1) + coderay (~> 1.1) + method_source (~> 1.0) + rake (13.0.6) + rspec (3.11.0) + rspec-core (~> 3.11.0) + rspec-expectations (~> 3.11.0) + rspec-mocks (~> 3.11.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.11.0) + rspec-mocks (3.11.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.11.0) + rspec-parameterized (0.5.1) + binding_ninja (>= 0.2.3) + parser + proc_to_ast + rspec (>= 2.13, < 4) + unparser + rspec-support (3.11.0) + unparser (0.6.5) + diff-lcs (~> 1.3) + parser (>= 3.1.0) + +PLATFORMS + x86_64-darwin-20 + x86_64-linux + +DEPENDENCIES + bundler (~> 2.2) + ipynbdiff! + pry (~> 0.14) + rake (~> 13.0) + rspec (~> 3.10) + rspec-parameterized (~> 0.5.1) + +BUNDLED WITH + 2.3.16 diff --git a/vendor/gems/ipynbdiff/LICENSE b/vendor/gems/ipynbdiff/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..e6de2f908640470f7423b29f745cf07679425688 --- /dev/null +++ b/vendor/gems/ipynbdiff/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016-2021 GitLab B.V. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/gems/ipynbdiff/README.md b/vendor/gems/ipynbdiff/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f046f678a4d168a35838256a9e31ea155c918863 --- /dev/null +++ b/vendor/gems/ipynbdiff/README.md @@ -0,0 +1,56 @@ +# IpynbDiff: Better diff for Jupyter Notebooks + +This is a simple diff tool that cleans up Jupyter notebooks, transforming each [notebook](example/1/from.ipynb) +into a [readable markdown file](example/1/from_html.md), keeping the output of cells, and running the +diff after. Markdowns are generated using an opinionated Jupyter to Markdown conversion. This means +that the entire file is readable on the diff. + +The result are diffs that are much easier to read: + +| Diff | IpynbDiff | +| ----------------------------------- | ----------------------------------------------------- | +| [Diff text](example/diff.txt) | [IpynbDiff text](example/ipynbdiff_percent.txt) | +|  |  | + +This started as a port of [ipynbdiff](https://gitlab.com/gitlab-org/incubation-engineering/mlops/poc/ipynbdiff), +but now has extended functionality although not working as git driver. + +## Usage + +### Generating diffs + +```ruby +IpynbDiff.diff(from_path, to_path, options) +``` + +Options: + +```ruby +@default_transform_options = { + preprocess_input: true, # Whether the input should be transformed + write_output_to: nil, # Pass a path to save the output to a file + format: :text, # These are the formats Diffy accepts https://github.com/samg/diffy + sources_are_files: false, # Weather to use the from/to as string or path to a file + raise_if_invalid_notebook: false, # Raises an error if the notebooks are invalid, otherwise returns nil + transform_options: @default_transform_options, # See below for transform options + diff_opts: { + include_diff_info: false # These are passed to Diffy https://github.com/samg/diffy + } +} +``` + +### Transforming the notebooks + +It might be necessary to have the transformed files in addition to the diff. + +```ruby +IpynbDiff.transform(notebook, options) +``` + +Options: + +```ruby +@default_transform_options = { + include_frontmatter: false, # Whether to include or not the notebook metadata (kernel, language, etc) +} +``` diff --git a/vendor/gems/ipynbdiff/ipynbdiff.gemspec b/vendor/gems/ipynbdiff/ipynbdiff.gemspec new file mode 100644 index 0000000000000000000000000000000000000000..9ace051b4966f135d1bca9e6f7b91934252313d6 --- /dev/null +++ b/vendor/gems/ipynbdiff/ipynbdiff.gemspec @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +lib = File.expand_path('lib/..', __dir__) +$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib) + +require 'lib/version' + +Gem::Specification.new do |s| + s.name = 'ipynbdiff' + s.version = IpynbDiff::VERSION + s.summary = 'Human Readable diffs for Jupyter Notebooks' + s.description = 'Better diff for Jupyter Notebooks by first preprocessing them and removing clutter' + s.authors = ['Eduardo Bonet'] + s.email = 'ebonet@gitlab.com' + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + s.files = Dir.chdir(File.expand_path(__dir__)) do + `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|example)/}) } + end + s.homepage = + 'https://gitlab.com/gitlab-org/incubation-engineering/mlops/rb-ipynbdiff' + s.license = 'MIT' + + s.require_paths = ['lib'] + + s.add_runtime_dependency 'diffy', '~> 3.3' + s.add_runtime_dependency 'json', '~> 2.5', '>= 2.5.1' + + s.add_development_dependency 'bundler', '~> 2.2' + s.add_development_dependency 'pry', '~> 0.14' + s.add_development_dependency 'rake', '~> 13.0' + s.add_development_dependency 'rspec', '~> 3.10' + s.add_development_dependency 'rspec-parameterized', '~> 0.5.1' +end diff --git a/vendor/gems/ipynbdiff/lib/diff.rb b/vendor/gems/ipynbdiff/lib/diff.rb new file mode 100644 index 0000000000000000000000000000000000000000..3554ac55d9936caba849bc2c2fa729e3d4946ba4 --- /dev/null +++ b/vendor/gems/ipynbdiff/lib/diff.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +# Custom differ for Jupyter Notebooks +module IpynbDiff + require 'delegate' + + # The result of a diff object + class Diff < SimpleDelegator + require 'diffy' + + attr_reader :from, :to + + def initialize(from, to, diffy_opts) + super(Diffy::Diff.new(from.as_text, to.as_text, **diffy_opts)) + + @from = from + @to = to + end + end +end diff --git a/vendor/gems/ipynbdiff/lib/ipynb_symbol_map.rb b/vendor/gems/ipynbdiff/lib/ipynb_symbol_map.rb new file mode 100644 index 0000000000000000000000000000000000000000..33e06aa8d189d925ae8eb67607a6e7aaf7db643d --- /dev/null +++ b/vendor/gems/ipynbdiff/lib/ipynb_symbol_map.rb @@ -0,0 +1,218 @@ +# frozen_string_literal: true + +module IpynbDiff + class InvalidTokenError < StandardError + end + + # Creates a symbol map for a ipynb file (JSON format) + class IpynbSymbolMap + class << self + def parse(notebook, objects_to_ignore = []) + IpynbSymbolMap.new(notebook, objects_to_ignore).parse('') + end + end + + attr_reader :current_line, :char_idx, :results + + WHITESPACE_CHARS = ["\t", "\r", ' ', "\n"].freeze + + VALUE_STOPPERS = [',', '[', ']', '{', '}', *WHITESPACE_CHARS].freeze + + def initialize(notebook, objects_to_ignore = []) + @chars = notebook.chars + @current_line = 0 + @char_idx = 0 + @results = {} + @objects_to_ignore = objects_to_ignore + end + + def parse(prefix = '.') + raise_if_file_ended + + skip_whitespaces + + if (c = current_char) == '"' + parse_string + elsif c == '[' + parse_array(prefix) + elsif c == '{' + parse_object(prefix) + else + parse_value + end + + results + end + + def parse_array(prefix) + # [1, 2, {"some": "object"}, [1]] + + i = 0 + + current_should_be '[' + + loop do + raise_if_file_ended + + break if skip_beginning(']') + + new_prefix = "#{prefix}.#{i}" + + add_result(new_prefix, current_line) + + parse(new_prefix) + + i += 1 + end + end + + def parse_object(prefix) + # {"name":"value", "another_name": [1, 2, 3]} + + current_should_be '{' + + loop do + raise_if_file_ended + + break if skip_beginning('}') + + prop_name = parse_string(return_value: true) + + next_and_skip_whitespaces + + current_should_be ':' + + next_and_skip_whitespaces + + if @objects_to_ignore.include? prop_name + skip + else + new_prefix = "#{prefix}.#{prop_name}" + + add_result(new_prefix, current_line) + + parse(new_prefix) + end + end + end + + def parse_string(return_value: false) + current_should_be '"' + init_idx = @char_idx + + loop do + increment_char_index + + raise_if_file_ended + + if current_char == '"' && !prev_backslash? + init_idx += 1 + break + end + end + + @chars[init_idx...@char_idx].join if return_value + end + + def add_result(key, line_number) + @results[key] = line_number + end + + def parse_value + increment_char_index until raise_if_file_ended || VALUE_STOPPERS.include?(current_char) + end + + def skip_whitespaces + while WHITESPACE_CHARS.include?(current_char) + raise_if_file_ended + check_for_new_line + increment_char_index + end + end + + def increment_char_index + @char_idx += 1 + end + + def next_and_skip_whitespaces + increment_char_index + skip_whitespaces + end + + def current_char + raise_if_file_ended + + @chars[@char_idx] + end + + def prev_backslash? + @chars[@char_idx - 1] == '\\' && @chars[@char_idx - 2] != '\\' + end + + def current_should_be(another_char) + raise InvalidTokenError unless current_char == another_char + end + + def check_for_new_line + @current_line += 1 if current_char == "\n" + end + + def raise_if_file_ended + @char_idx >= @chars.size && raise(InvalidTokenError) + end + + def skip + raise_if_file_ended + + skip_whitespaces + + if (c = current_char) == '"' + parse_string + elsif c == '[' + skip_array + elsif c == '{' + skip_object + else + parse_value + end + end + + def skip_array + loop do + raise_if_file_ended + + break if skip_beginning(']') + + skip + end + end + + def skip_object + loop do + raise_if_file_ended + + break if skip_beginning('}') + + parse_string + + next_and_skip_whitespaces + + current_should_be ':' + + next_and_skip_whitespaces + + skip + end + end + + def skip_beginning(closing_char) + check_for_new_line + + next_and_skip_whitespaces + + return true if current_char == closing_char + + next_and_skip_whitespaces if current_char == ',' + end + end +end diff --git a/vendor/gems/ipynbdiff/lib/ipynbdiff.rb b/vendor/gems/ipynbdiff/lib/ipynbdiff.rb new file mode 100644 index 0000000000000000000000000000000000000000..1765e434bf9539eb84a1365ae214c8a4bfb1d7e6 --- /dev/null +++ b/vendor/gems/ipynbdiff/lib/ipynbdiff.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Human Readable Jupyter Diffs +module IpynbDiff + require 'transformer' + require 'diff' + + def self.diff(from, to, raise_if_invalid_nb: false, include_frontmatter: false, hide_images: false, diffy_opts: {}) + transformer = Transformer.new(include_frontmatter: include_frontmatter, hide_images: hide_images) + + Diff.new(transformer.transform(from), transformer.transform(to), diffy_opts) + rescue InvalidNotebookError + raise if raise_if_invalid_nb + end + + def self.transform(notebook, raise_errors: false, include_frontmatter: true, hide_images: false) + return unless notebook + + Transformer.new(include_frontmatter: include_frontmatter, hide_images: hide_images).transform(notebook).as_text + rescue InvalidNotebookError + raise if raise_errors + end +end diff --git a/vendor/gems/ipynbdiff/lib/output_transformer.rb b/vendor/gems/ipynbdiff/lib/output_transformer.rb new file mode 100644 index 0000000000000000000000000000000000000000..88728df2f173e3f188ede525986dbc073f809636 --- /dev/null +++ b/vendor/gems/ipynbdiff/lib/output_transformer.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +module IpynbDiff + # Transforms Jupyter output data into markdown + class OutputTransformer + require 'symbolized_markdown_helper' + include SymbolizedMarkdownHelper + + HIDDEN_IMAGE_OUTPUT = ' [Hidden Image Output]' + + ORDERED_KEYS = { + 'execute_result' => %w[image/png image/svg+xml image/jpeg text/markdown text/latex text/plain], + 'display_data' => %w[image/png image/svg+xml image/jpeg text/markdown text/latex], + 'stream' => %w[text] + }.freeze + + def initialize(hide_images: false) + @hide_images = hide_images + end + + def transform(output, symbol) + transformed = case (output_type = output['output_type']) + when 'error' + transform_error(output['traceback'], symbol / 'traceback') + when 'execute_result', 'display_data' + transform_non_error(ORDERED_KEYS[output_type], output['data'], symbol / 'data') + when 'stream' + transform_element('text', output['text'], symbol) + end + + transformed ? decorate_output(transformed, output, symbol) : [] + end + + def decorate_output(output_rows, output, symbol) + [ + _, + _(symbol, %(%%%% Output: #{output['output_type']})), + _, + *output_rows + ] + end + + def transform_error(traceback, symbol) + traceback.map.with_index do |t, idx| + t.split("\n").map do |l| + _(symbol / idx, l.gsub(/\[[0-9][0-9;]*m/, '').sub("\u001B", ' ').gsub(/\u001B/, '').rstrip) + end + end + end + + def transform_non_error(accepted_keys, elements, symbol) + accepted_keys.filter { |key| elements.key?(key) }.map do |key| + transform_element(key, elements[key], symbol) + end + end + + def transform_element(output_type, output_element, symbol_prefix) + new_symbol = symbol_prefix / output_type + case output_type + when 'image/png', 'image/jpeg' + transform_image(output_type + ';base64', output_element, new_symbol) + when 'image/svg+xml' + transform_image(output_type + ';utf8', output_element, new_symbol) + when 'text/markdown', 'text/latex', 'text/plain', 'text' + transform_text(output_element, new_symbol) + end + end + + def transform_image(image_type, image_content, symbol) + return _(nil, HIDDEN_IMAGE_OUTPUT) if @hide_images + + lines = image_content.is_a?(Array) ? image_content : [image_content] + + single_line = lines.map(&:strip).join.gsub(/\s+/, ' ') + + _(symbol, " ") + end + + def transform_text(text_content, symbol) + symbolize_array(symbol, text_content) { |l| " #{l.rstrip}" } + end + end +end diff --git a/vendor/gems/ipynbdiff/lib/symbolized_markdown_helper.rb b/vendor/gems/ipynbdiff/lib/symbolized_markdown_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..918666ed899d920296d025d74e7d566ad17200f1 --- /dev/null +++ b/vendor/gems/ipynbdiff/lib/symbolized_markdown_helper.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module IpynbDiff + # Helper functions + module SymbolizedMarkdownHelper + + def _(symbol = nil, content = '') + { symbol: symbol, content: content } + end + + def symbolize_array(symbol, content, &block) + if content.is_a?(Array) + content.map.with_index { |l, idx| _(symbol / idx, block.call(l)) } + else + _(symbol, content) + end + end + end + + # Simple wrapper for a string + class JsonSymbol < String + def /(other) + JsonSymbol.new((other.is_a?(Array) ? [self, *other] : [self, other]).join('.')) + end + end +end diff --git a/vendor/gems/ipynbdiff/lib/transformed_notebook.rb b/vendor/gems/ipynbdiff/lib/transformed_notebook.rb new file mode 100644 index 0000000000000000000000000000000000000000..7a8edf7c22f16ed2a38a7bd6bb47aae4a531c6eb --- /dev/null +++ b/vendor/gems/ipynbdiff/lib/transformed_notebook.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module IpynbDiff + # Notebook that was transformed into md, including location of source cells + class TransformedNotebook + attr_reader :blocks + + def as_text + @blocks.map { |b| b[:content] }.join("\n") + end + + private + + def initialize(lines = [], symbol_map = {}) + @blocks = lines.map do |line| + { content: line[:content], source_symbol: (symbol = line[:symbol]), source_line: symbol && symbol_map[symbol] } + end + end + end +end diff --git a/vendor/gems/ipynbdiff/lib/transformer.rb b/vendor/gems/ipynbdiff/lib/transformer.rb new file mode 100644 index 0000000000000000000000000000000000000000..153d821db2797df57858210eb8d820744ff8ca99 --- /dev/null +++ b/vendor/gems/ipynbdiff/lib/transformer.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +module IpynbDiff + class InvalidNotebookError < StandardError + end + + # Returns a markdown version of the Jupyter Notebook + class Transformer + require 'json' + require 'yaml' + require 'output_transformer' + require 'symbolized_markdown_helper' + require 'ipynb_symbol_map' + require 'transformed_notebook' + include SymbolizedMarkdownHelper + + @include_frontmatter = true + @objects_to_ignore = ['application/javascript', 'application/vnd.holoviews_load.v0+json'] + + def initialize(include_frontmatter: true, hide_images: false) + @include_frontmatter = include_frontmatter + @hide_images = hide_images + @output_transformer = OutputTransformer.new(hide_images: hide_images) + end + + def validate_notebook(notebook) + notebook_json = JSON.parse(notebook) + + return notebook_json if notebook_json.key?('cells') + + raise InvalidNotebookError + rescue JSON::ParserError + raise InvalidNotebookError + end + + def transform(notebook) + return TransformedNotebook.new unless notebook + + notebook_json = validate_notebook(notebook) + transformed = transform_document(notebook_json) + symbol_map = IpynbSymbolMap.parse(notebook) + + TransformedNotebook.new(transformed, symbol_map) + end + + def transform_document(notebook) + symbol = JsonSymbol.new('.cells') + + transformed_blocks = notebook['cells'].map.with_index do |cell, idx| + decorate_cell(transform_cell(cell, notebook, symbol / idx), cell, symbol / idx) + end + + transformed_blocks.prepend(transform_metadata(notebook)) if @include_frontmatter + transformed_blocks.flatten + end + + def decorate_cell(rows, cell, symbol) + tags = cell['metadata']&.fetch('tags', []) + type = cell['cell_type'] || 'raw' + + [ + _(symbol, %(%% Cell type:#{type} id:#{cell['id']} tags:#{tags&.join(',')})), + _, + rows, + _ + ] + end + + def transform_cell(cell, notebook, symbol) + cell['cell_type'] == 'code' ? transform_code_cell(cell, notebook, symbol) : transform_text_cell(cell, symbol) + end + + def transform_code_cell(cell, notebook, symbol) + [ + _(symbol / 'source', %(``` #{notebook.dig('metadata', 'kernelspec', 'language') || ''})), + symbolize_array(symbol / 'source', cell['source'], &:rstrip), + _(nil, '```'), + cell['outputs'].map.with_index do |output, idx| + @output_transformer.transform(output, symbol / ['outputs', idx]) + end + ] + end + + def transform_text_cell(cell, symbol) + symbolize_array(symbol / 'source', cell['source'], &:rstrip) + end + + def transform_metadata(notebook_json) + as_yaml = { + 'jupyter' => { + 'kernelspec' => notebook_json['metadata']['kernelspec'], + 'language_info' => notebook_json['metadata']['language_info'], + 'nbformat' => notebook_json['nbformat'], + 'nbformat_minor' => notebook_json['nbformat_minor'] + } + }.to_yaml + + as_yaml.split("\n").map { |l| _(nil, l) }.append(_(nil, '---'), _) + end + end +end diff --git a/vendor/gems/ipynbdiff/lib/version.rb b/vendor/gems/ipynbdiff/lib/version.rb new file mode 100644 index 0000000000000000000000000000000000000000..1451bb4ef329ee0a563aa79a9cc2af7130333253 --- /dev/null +++ b/vendor/gems/ipynbdiff/lib/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module IpynbDiff + VERSION = '0.4.7' +end diff --git a/vendor/gems/ipynbdiff/spec/ipynb_symbol_map_spec.rb b/vendor/gems/ipynbdiff/spec/ipynb_symbol_map_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..a002fc370f546dc1e34b73a8e7ee14617a2d4a4e --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/ipynb_symbol_map_spec.rb @@ -0,0 +1,165 @@ +# frozen_string_literal: true + +require 'rspec' +require 'json' +require 'rspec-parameterized' +require 'ipynb_symbol_map' + +describe IpynbDiff::IpynbSymbolMap do + def res(*cases) + cases&.to_h || [] + end + + describe '#parse_string' do + using RSpec::Parameterized::TableSyntax + + let(:mapper) { IpynbDiff::IpynbSymbolMap.new(input) } + + where(:input, :result) do + # Empty string + '""' | '' + # Some string with quotes + '"he\nll\"o"' | 'he\nll\"o' + end + + with_them do + it { expect(mapper.parse_string(return_value: true)).to eq(result) } + it { expect(mapper.parse_string).to be_nil } + it { expect(mapper.results).to be_empty } + end + + it 'raises if invalid string' do + mapper = IpynbDiff::IpynbSymbolMap.new('"') + + expect { mapper.parse_string }.to raise_error(IpynbDiff::InvalidTokenError) + end + + end + + describe '#parse_object' do + using RSpec::Parameterized::TableSyntax + + let(:mapper) { IpynbDiff::IpynbSymbolMap.new(notebook, objects_to_ignore) } + + before do + mapper.parse_object('') + end + + where(:notebook, :objects_to_ignore, :result) do + # Empty object + '{ }' | [] | res + # Object with string + '{ "hello" : "world" }' | [] | res(['.hello', 0]) + # Object with boolean + '{ "hello" : true }' | [] | res(['.hello', 0]) + # Object with integer + '{ "hello" : 1 }' | [] | res(['.hello', 0]) + # Object with 2 properties in the same line + '{ "hello" : "world" , "my" : "bad" }' | [] | res(['.hello', 0], ['.my', 0]) + # Object with 2 properties in the different lines line + "{ \"hello\" : \"world\" , \n \n \"my\" : \"bad\" }" | [] | res(['.hello', 0], ['.my', 2]) + # Object with 2 properties, but one is ignored + "{ \"hello\" : \"world\" , \n \n \"my\" : \"bad\" }" | ['hello'] | res(['.my', 2]) + end + + with_them do + it { expect(mapper.results).to include(result) } + end + end + + describe '#parse_array' do + using RSpec::Parameterized::TableSyntax + + where(:notebook, :result) do + # Empty Array + '[]' | res + # Array with string value + '["a"]' | res(['.0', 0]) + # Array with boolean + '[ true ]' | res(['.0', 0]) + # Array with integer + '[ 1 ]' | res(['.0', 0]) + # Two values on the same line + '["a", "b"]' | res(['.0', 0], ['.1', 0]) + # With line breaks' + "[\n \"a\" \n , \n \"b\" ]" | res(['.0', 1], ['.1', 3]) + end + + let(:mapper) { IpynbDiff::IpynbSymbolMap.new(notebook) } + + before do + mapper.parse_array('') + end + + with_them do + it { expect(mapper.results).to match_array(result) } + end + end + + describe '#skip_object' do + subject { IpynbDiff::IpynbSymbolMap.parse(JSON.pretty_generate(source)) } + end + + describe '#parse' do + + let(:objects_to_ignore) { [] } + + subject { IpynbDiff::IpynbSymbolMap.parse(JSON.pretty_generate(source), objects_to_ignore) } + + context 'Empty object' do + let(:source) { {} } + + it { is_expected.to be_empty } + end + + context 'Object with inner object and number' do + let(:source) { { obj1: { obj2: 1 } } } + + it { is_expected.to match_array(res(['.obj1', 1], ['.obj1.obj2', 2])) } + end + + context 'Object with inner object and number, string and array with object' do + let(:source) { { obj1: { obj2: [123, 2, true], obj3: "hel\nlo", obj4: true, obj5: 123, obj6: 'a' } } } + + it do + is_expected.to match_array( + res(['.obj1', 1], + ['.obj1.obj2', 2], + ['.obj1.obj2.0', 3], + ['.obj1.obj2.1', 4], + ['.obj1.obj2.2', 5], + ['.obj1.obj3', 7], + ['.obj1.obj4', 8], + ['.obj1.obj5', 9], + ['.obj1.obj6', 10]) + ) + end + end + + context 'When index is exceeded because of failure' do + it 'raises an exception' do + source = '{"\\a": "a\""}' + + mapper = IpynbDiff::IpynbSymbolMap.new(source) + + expect(mapper).to receive(:prev_backslash?).at_least(1).time.and_return(false) + + expect { mapper.parse('') }.to raise_error(IpynbDiff::InvalidTokenError) + end + end + + context 'Object with inner object and number, string and array with object' do + let(:source) { { obj1: { obj2: [123, 2, true], obj3: "hel\nlo", obj4: true, obj5: 123, obj6: { obj7: 'a' } } } } + let(:objects_to_ignore) { %w(obj2 obj6) } + it do + is_expected.to match_array( + res(['.obj1', 1], + ['.obj1.obj3', 7], + ['.obj1.obj4', 8], + ['.obj1.obj5', 9], + ) + ) + end + end + end +end diff --git a/vendor/gems/ipynbdiff/spec/ipynbdiff_spec.rb b/vendor/gems/ipynbdiff/spec/ipynbdiff_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..1c2a2188edf7f1ab987e8aadcd6d2cf36787eb42 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/ipynbdiff_spec.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +require 'ipynbdiff' +require 'rspec' +require 'rspec-parameterized' + +BASE_PATH = File.join(File.expand_path(File.dirname(__FILE__)), 'testdata') + +describe IpynbDiff do + def diff_signs(diff) + diff.to_s(:text).scan(/.*\n/).map { |l| l[0] }.join('') + end + + describe 'diff' do + let(:from_path) { File.join(BASE_PATH, 'from.ipynb') } + let(:to_path) { File.join(BASE_PATH,'to.ipynb') } + let(:from) { File.read(from_path) } + let(:to) { File.read(to_path) } + let(:include_frontmatter) { false } + let(:hide_images) { false } + + subject { IpynbDiff.diff(from, to, include_frontmatter: include_frontmatter, hide_images: hide_images) } + + context 'if preprocessing is active' do + it 'html tables are stripped' do + is_expected.to_not include('<td>') + end + end + + context 'when to is nil' do + let(:to) { nil } + let(:from_path) { File.join(BASE_PATH, 'only_md', 'input.ipynb') } + + it 'all lines are removals' do + expect(diff_signs(subject)).to eq('-----') + end + end + + context 'when to is nil' do + let(:from) { nil } + let(:to_path) { File.join(BASE_PATH, 'only_md', 'input.ipynb') } + + it 'all lines are additions' do + expect(diff_signs(subject)).to eq('+++++') + end + end + + context 'When include_frontmatter is true' do + let(:include_frontmatter) { true } + + it 'should show changes metadata in the metadata' do + expect(subject.to_s(:text)).to include('+ display_name: New Python 3 (ipykernel)') + end + end + + context 'When hide_images is true' do + let(:hide_images) { true } + + it 'hides images' do + expect(subject.to_s(:text)).to include(' [Hidden Image Output]') + end + end + + context 'When include_frontmatter is false' do + it 'should drop metadata from the diff' do + expect(subject.to_s(:text)).to_not include('+ display_name: New Python 3 (ipykernel)') + end + end + + context 'when either notebook can not be processed' do + using RSpec::Parameterized::TableSyntax + + where(:ctx, :from, :to) do + 'because from is invalid' | 'a' | nil + 'because from does not have the cell tag' | '{"metadata":[]}' | nil + 'because to is invalid' | nil | 'a' + 'because to does not have the cell tag' | nil | '{"metadata":[]}' + end + + with_them do + it { is_expected.to be_nil } + end + end + end + + describe 'transform' do + [nil, 'a', '{"metadata":[]}'].each do |invalid_nb| + context "when json is invalid (#{invalid_nb || 'nil'})" do + it 'is nil' do + expect(IpynbDiff.transform(invalid_nb)).to be_nil + end + end + end + + context 'options' do + let(:include_frontmatter) { false } + let(:hide_images) { false } + + subject do + IpynbDiff.transform(File.read(File.join(BASE_PATH, 'from.ipynb')), + include_frontmatter: include_frontmatter, + hide_images: hide_images) + end + + context 'include_frontmatter is false' do + it { is_expected.to_not include('display_name: Python 3 (ipykernel)') } + end + + context 'include_frontmatter is true' do + let(:include_frontmatter) { true } + + it { is_expected.to include('display_name: Python 3 (ipykernel)') } + end + + context 'hide_images is false' do + it { is_expected.not_to include('[Hidden Image Output]') } + end + + context 'hide_images is true' do + let(:hide_images) { true } + + it { is_expected.to include(' [Hidden Image Output]') } + end + end + end +end diff --git a/vendor/gems/ipynbdiff/spec/testdata/backslash_as_last_char/expected.md b/vendor/gems/ipynbdiff/spec/testdata/backslash_as_last_char/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..299e286c679960fc4b6a00507f3d74d9463b50f6 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/backslash_as_last_char/expected.md @@ -0,0 +1,7 @@ +%% Cell type:markdown id: tags: + +\ + +%% Cell type:markdown id: tags: + +a diff --git a/vendor/gems/ipynbdiff/spec/testdata/backslash_as_last_char/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/backslash_as_last_char/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..6fa29ae28de0d1d8161dc6050c6259f29999120c --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/backslash_as_last_char/expected_symbols.txt @@ -0,0 +1,7 @@ +.cells.0 + +.cells.0.source.0 + +.cells.1 + +.cells.1.source.0 diff --git a/vendor/gems/ipynbdiff/spec/testdata/backslash_as_last_char/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/backslash_as_last_char/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..0714044e3aeb7690e884565283484c0fa336b5ab --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/backslash_as_last_char/input.ipynb @@ -0,0 +1,16 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "\\" + ] + }, + { + "cell_type": "markdown", + "source": [ + "a" + ] + } + ] +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/error_output/expected.md b/vendor/gems/ipynbdiff/spec/testdata/error_output/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..5be645de9c9718b055de5f93cc709c37a9f8063c --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/error_output/expected.md @@ -0,0 +1,16 @@ +%% Cell type:code id:5 tags: + +``` python +# A cell that has an error +y = sin(x) +``` + +%%%% Output: error + + --------------------------------------------------------------------------- + NameError Traceback (most recent call last) + /var/folders/cq/l637k4x13gx6y9p_gfs4c_gc0000gn/T/ipykernel_72857/3962062127.py in <module> + 1 # A cell that has an error + ----> 2 y = sin(x) + + NameError: name 'sin' is not defined diff --git a/vendor/gems/ipynbdiff/spec/testdata/error_output/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/error_output/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..75e35d123d058bd14291150fefd1b198c9216653 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/error_output/expected_symbols.txt @@ -0,0 +1,16 @@ +.cells.0 + +.cells.0.source +.cells.0.source.0 +.cells.0.source.1 + + +.cells.0.outputs.0 + +.cells.0.outputs.0.traceback.0 +.cells.0.outputs.0.traceback.1 +.cells.0.outputs.0.traceback.2 +.cells.0.outputs.0.traceback.2 +.cells.0.outputs.0.traceback.2 +.cells.0.outputs.0.traceback.2 +.cells.0.outputs.0.traceback.3 diff --git a/vendor/gems/ipynbdiff/spec/testdata/error_output/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/error_output/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..45ee81a0e2df7686c3998d337e8eb2e11d0c3a93 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/error_output/input.ipynb @@ -0,0 +1,32 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 7, + "id": "5", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'sin' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/var/folders/cq/l637k4x13gx6y9p_gfs4c_gc0000gn/T/ipykernel_72857/3962062127.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# A cell that has an error\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'sin' is not defined" + ] + } + ], + "source": [ + "# A cell that has an error\n", + "y = sin(x)" + ] + } + ], + "metadata": { + "kernelspec": { + "language": "python" + } + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/from.ipynb b/vendor/gems/ipynbdiff/spec/testdata/from.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a731c9bfffdfcbf8c3700f3518f5986728cc35d3 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/from.ipynb @@ -0,0 +1,198 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0aac5da7-745c-4eda-847a-3d0d07a1bb9b", + "metadata": { + "tags": [] + }, + "source": [ + "# This is a markdown cell\n", + "\n", + "This paragraph has\n", + "With\n", + "Many\n", + "Lines. How we will he handle MR notes?\n", + "\n", + "But I can add another paragraph" + ] + }, + { + "cell_type": "raw", + "id": "faecea5b-de0a-49fa-9a3a-61c2add652da", + "metadata": {}, + "source": [ + "This is a raw cell\n", + "With\n", + "Multiple lines" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "893ca2c0-ab75-4276-9dad-be1c40e16e8a", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0d707fb5-226f-46d6-80bd-489ebfb8905c", + "metadata": {}, + "outputs": [], + "source": [ + "np.random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "35467fcf-28b1-4c7b-bb09-4cb192c35293", + "metadata": { + "tags": [ + "senoid" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[<matplotlib.lines.Line2D at 0x123e39370>]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "some_invalid_base64_image_here\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "x = np.linspace(0, 4*np.pi,50)\n", + "y = np.sin(x)\n", + "\n", + "plt.plot(x, y)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "dc1178cd-c46d-4da3-9ab5-08f000699884", + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.DataFrame({\"x\": x, \"y\": y})" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6e749b4f-b409-4700-870f-f68c39462490", + "metadata": { + "tags": [ + "some-table" + ] + }, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>x</th>\n", + " <th>y</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>0.000000</td>\n", + " <td>0.000000</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>0.256457</td>\n", + " <td>0.253655</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " x y\n", + "0 0.000000 0.000000\n", + "1 0.256457 0.253655" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df[:2]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ddef5ef-94a3-4afd-9c70-ddee9694f512", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + }, + "toc-showtags": true + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/hide_images/expected.md b/vendor/gems/ipynbdiff/spec/testdata/hide_images/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..89a812740a6d46d283ed415b166a631aa404e0c3 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/hide_images/expected.md @@ -0,0 +1,12 @@ +%% Cell type:code id:5 tags:senoid + +``` python +``` + +%%%% Output: display_data + + [Hidden Image Output] + +%%%% Output: display_data + + [Hidden Image Output] diff --git a/vendor/gems/ipynbdiff/spec/testdata/hide_images/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/hide_images/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..b94e9538f58d879f6d90fd5e8e77e1cd79c06f0d --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/hide_images/expected_symbols.txt @@ -0,0 +1,12 @@ +.cells.0 + +.cells.0.source + + +.cells.0.outputs.0 + + + +.cells.0.outputs.1 + + diff --git a/vendor/gems/ipynbdiff/spec/testdata/hide_images/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/hide_images/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..dab0e5bb9cf517485c65696cb9430156694ee6da --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/hide_images/input.ipynb @@ -0,0 +1,45 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "5", + "metadata": { + "tags": [ + "senoid" + ] + }, + "outputs": [ + { + "data": { + "image/png": "this_is_an_invalid_hash_for_testing_purposes\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\"><circle cx=\"50\" cy=\"50\" r=\"50\"/></svg>", + "text/plain": [ + "<IPython.core.display.SVG object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + ] + } + ], + "metadata": { + "kernelspec": { + "language": "python" + } + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/ignore_html_output/expected.md b/vendor/gems/ipynbdiff/spec/testdata/ignore_html_output/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..456224f3afff830d605dba31ed72436ba02ac685 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/ignore_html_output/expected.md @@ -0,0 +1,11 @@ +%% Cell type:code id:5 tags:some-table + +``` python +df[:2] +``` + +%%%% Output: execute_result + + x y + 0 0.000000 0.000000 + 1 0.256457 0.507309 diff --git a/vendor/gems/ipynbdiff/spec/testdata/ignore_html_output/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/ignore_html_output/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..fa9d412c6dce5812ce27bd3df909625538b34d6a --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/ignore_html_output/expected_symbols.txt @@ -0,0 +1,11 @@ +.cells.0 + +.cells.0.source +.cells.0.source.0 + + +.cells.0.outputs.0 + +.cells.0.outputs.0.data.text/plain.0 +.cells.0.outputs.0.data.text/plain.1 +.cells.0.outputs.0.data.text/plain.2 diff --git a/vendor/gems/ipynbdiff/spec/testdata/ignore_html_output/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/ignore_html_output/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..26117a7893413bbdf7eb57b613c1bec4a07d5b40 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/ignore_html_output/input.ipynb @@ -0,0 +1,74 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "id": "5", + "metadata": { + "tags": [ + "some-table" + ] + }, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>x</th>\n", + " <th>y</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>0.000000</td>\n", + " <td>0.000000</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>0.256457</td>\n", + " <td>0.507309</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " x y\n", + "0 0.000000 0.000000\n", + "1 0.256457 0.507309" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df[:2]" + ] + } + ], + "metadata": { + "kernelspec": { + "language": "python" + } + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/latex_output/expected.md b/vendor/gems/ipynbdiff/spec/testdata/latex_output/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..add84ed26a03a089e69fdf16ce6552caf818d9f6 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/latex_output/expected.md @@ -0,0 +1,10 @@ +%% Cell type:code id:5 tags: + +``` python +from IPython.display import display, Math +display(Math(r'Dims: {}x{}m \\ Area: {}m^2 \\ Volume: {}m^3'.format(1, round(2,2), 3, 4))) +``` + +%%%% Output: display_data + + $\displaystyle Dims: 1x2m \\ Area: 3m^2 \\ Volume: 4m^3$ diff --git a/vendor/gems/ipynbdiff/spec/testdata/latex_output/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/latex_output/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..9407e6db7025d0f37d77bd6a910ea28827888a19 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/latex_output/expected_symbols.txt @@ -0,0 +1,10 @@ +.cells.0 + +.cells.0.source +.cells.0.source.0 +.cells.0.source.1 + + +.cells.0.outputs.0 + +.cells.0.outputs.0.data.text/latex.0 diff --git a/vendor/gems/ipynbdiff/spec/testdata/latex_output/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/latex_output/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..f8ff3e72beb4d0e2104c8a1d81fd561e92fa2d38 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/latex_output/input.ipynb @@ -0,0 +1,34 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 4, + "id": "5", + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle Dims: 1x2m \\\\ Area: 3m^2 \\\\ Volume: 4m^3$" + ], + "text/plain": [ + "<IPython.core.display.Math object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import display, Math\n", + "display(Math(r'Dims: {}x{}m \\\\ Area: {}m^2 \\\\ Volume: {}m^3'.format(1, round(2,2), 3, 4)))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/multiline_png_output/expected.md b/vendor/gems/ipynbdiff/spec/testdata/multiline_png_output/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..4a880d8ce18e672c787ca7070fe9d8aa5813d952 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/multiline_png_output/expected.md @@ -0,0 +1,9 @@ +%% Cell type:code id:5 tags: + +``` +Some Image +``` + +%%%% Output: display_data + +  diff --git a/vendor/gems/ipynbdiff/spec/testdata/multiline_png_output/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/multiline_png_output/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..26e11781ec11a3d64fa8963a39e1665296f5912b --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/multiline_png_output/expected_symbols.txt @@ -0,0 +1,9 @@ +.cells.0 + +.cells.0.source +.cells.0.source.0 + + +.cells.0.outputs.0 + +.cells.0.outputs.0.data.image/png diff --git a/vendor/gems/ipynbdiff/spec/testdata/multiline_png_output/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/multiline_png_output/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..4d19a504553104c7d9406d2492d1bb3cb3d852a1 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/multiline_png_output/input.ipynb @@ -0,0 +1,25 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "5", + "metadata": { + }, + "outputs": [ + { + "data": { + "image/png": [ + "this_is_an_invalid_hash_for_testing_purposes" + ] + }, + "output_type": "display_data" + } + ], + "source": [ + "Some Image" + ] + } + ], + "metadata": { + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/no_cells/expected.md b/vendor/gems/ipynbdiff/spec/testdata/no_cells/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..b7c09c51fb86baa7c4f095f0ba048a6bac316564 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/no_cells/expected.md @@ -0,0 +1,19 @@ +--- +jupyter: + kernelspec: + display_name: Python 3 (ipykernel) + language: python + name: python3 + language_info: + codemirror_mode: + name: ipython + version: 3 + file_extension: ".py" + mimetype: text/x-python + name: python + nbconvert_exporter: python + pygments_lexer: ipython3 + version: 3.9.7 + nbformat: 4 + nbformat_minor: 5 +--- diff --git a/vendor/gems/ipynbdiff/spec/testdata/no_cells/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/no_cells/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..a60f3032882aa8c0946871f1d556aaafa6424bc3 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/no_cells/expected_symbols.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/vendor/gems/ipynbdiff/spec/testdata/no_cells/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/no_cells/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..c2ba0ebf50ab974e432adf68b6a907daeabf5e83 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/no_cells/input.ipynb @@ -0,0 +1,25 @@ +{ + "cells": [], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + }, + "toc-showtags": true + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/no_cells_no_metadata/expected.md b/vendor/gems/ipynbdiff/spec/testdata/no_cells_no_metadata/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/vendor/gems/ipynbdiff/spec/testdata/no_cells_no_metadata/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/no_cells_no_metadata/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/vendor/gems/ipynbdiff/spec/testdata/no_cells_no_metadata/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/no_cells_no_metadata/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..c2ba0ebf50ab974e432adf68b6a907daeabf5e83 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/no_cells_no_metadata/input.ipynb @@ -0,0 +1,25 @@ +{ + "cells": [], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + }, + "toc-showtags": true + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/no_metadata_on_cell/expected.md b/vendor/gems/ipynbdiff/spec/testdata/no_metadata_on_cell/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..d9d72bf8f769f8eb2e940ff16cb13aad28fd1255 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/no_metadata_on_cell/expected.md @@ -0,0 +1,13 @@ +%% Cell type:markdown id:1 tags: + +# A + +B + +%% Cell type:code id:3 tags: + +``` python +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +``` diff --git a/vendor/gems/ipynbdiff/spec/testdata/no_metadata_on_cell/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/no_metadata_on_cell/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..a7000494a1b81dde43693be07b964c24cc665625 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/no_metadata_on_cell/expected_symbols.txt @@ -0,0 +1,13 @@ +.cells.0 + +.cells.0.source.0 +.cells.0.source.1 +.cells.0.source.2 + +.cells.1 + +.cells.1.source +.cells.1.source.0 +.cells.1.source.1 +.cells.1.source.2 + diff --git a/vendor/gems/ipynbdiff/spec/testdata/no_metadata_on_cell/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/no_metadata_on_cell/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..62060124a2ab805561f4ea68e7858507fd81d51e --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/no_metadata_on_cell/input.ipynb @@ -0,0 +1,29 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1", + "source": [ + "# A\n", + "\n", + "B" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3", + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + } + ], + "metadata": { + "kernelspec": { + "language": "python" + } + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code/expected.md b/vendor/gems/ipynbdiff/spec/testdata/only_code/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..124b8217a6a87fe51f423ba50b9a22ffb6076002 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code/expected.md @@ -0,0 +1,7 @@ +%% Cell type:code id:3 tags: + +``` python +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +``` diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/only_code/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..59b111031950bc0eaa45dad59c27ade9f89ceaec --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code/expected_symbols.txt @@ -0,0 +1,7 @@ +.cells.0 + +.cells.0.source +.cells.0.source.0 +.cells.0.source.1 +.cells.0.source.2 + diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/only_code/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a93108dccb8c70fcba3764f404326a0278a04fbd --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code/input.ipynb @@ -0,0 +1,21 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + } + ], + "metadata": { + "kernelspec": { + "language": "python" + } + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code_no_kernelspec/expected.md b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_kernelspec/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..9100045e0f56cb00f37f083eb88a4db095d21839 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_kernelspec/expected.md @@ -0,0 +1,5 @@ +%% Cell type:code id:3 tags: + +``` + +``` diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code_no_kernelspec/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_kernelspec/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..5f9ad320b8c1e74e792166de5310cbe454cef67c --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_kernelspec/expected_symbols.txt @@ -0,0 +1,5 @@ +.cells.0 + +.cells.0.source +.cells.0.source + diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code_no_kernelspec/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_kernelspec/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..c3ff71057a61c8bda1c097ee4bcef4edba6150b1 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_kernelspec/input.ipynb @@ -0,0 +1,12 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "3", + "source": "", + "outputs": [] + } + ], + "metadata": {} +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code_no_language/expected.md b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_language/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..9100045e0f56cb00f37f083eb88a4db095d21839 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_language/expected.md @@ -0,0 +1,5 @@ +%% Cell type:code id:3 tags: + +``` + +``` diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code_no_language/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_language/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..5f9ad320b8c1e74e792166de5310cbe454cef67c --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_language/expected_symbols.txt @@ -0,0 +1,5 @@ +.cells.0 + +.cells.0.source +.cells.0.source + diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code_no_language/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_language/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..fb16b106cbe30ab817b258553051d6ddcc86c7dc --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_language/input.ipynb @@ -0,0 +1,14 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "3", + "source": "", + "outputs": [] + } + ], + "metadata": { + "kernelspec": {} + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code_no_metadata/expected.md b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_metadata/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..9100045e0f56cb00f37f083eb88a4db095d21839 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_metadata/expected.md @@ -0,0 +1,5 @@ +%% Cell type:code id:3 tags: + +``` + +``` diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code_no_metadata/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_metadata/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..5f9ad320b8c1e74e792166de5310cbe454cef67c --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_metadata/expected_symbols.txt @@ -0,0 +1,5 @@ +.cells.0 + +.cells.0.source +.cells.0.source + diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_code_no_metadata/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_metadata/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..364c080168ba9d4e9d36ee2cadf8d4f4e333920e --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_code_no_metadata/input.ipynb @@ -0,0 +1,11 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "3", + "source": "", + "outputs": [] + } + ] +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_md/expected.md b/vendor/gems/ipynbdiff/spec/testdata/only_md/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..bdf4db5aea59e141c35e46f0b9d60eecce2bd8ae --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_md/expected.md @@ -0,0 +1,5 @@ +%% Cell type:markdown id:1 tags:hello,world + +# A + +B diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_md/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/only_md/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..d3d6d526fc3a350d96f326e102cd26f549d43a40 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_md/expected_symbols.txt @@ -0,0 +1,5 @@ +.cells.0 + +.cells.0.source.0 +.cells.0.source.1 +.cells.0.source.2 diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_md/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/only_md/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..9d6b550af31b7575fafd18827ac0751cb9140f17 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_md/input.ipynb @@ -0,0 +1,21 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1", + "metadata": { + "tags": [ + "hello", + "world" + ] + }, + "source": [ + "# A\n", + "\n", + "B" + ] + } + ], + "metadata": { + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_raw/expected.md b/vendor/gems/ipynbdiff/spec/testdata/only_raw/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..91c476e843ba67985be50c57139bdc08293f7da2 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_raw/expected.md @@ -0,0 +1,4 @@ +%% Cell type:raw id:2 tags: + +A +B diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_raw/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/only_raw/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..bceaf355c2f68cbd018165d1ca26a8dda78397e4 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_raw/expected_symbols.txt @@ -0,0 +1,4 @@ +.cells.0 + +.cells.0.source.0 +.cells.0.source.1 diff --git a/vendor/gems/ipynbdiff/spec/testdata/only_raw/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/only_raw/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..750e1bba6150d541f89d2309a50b8e99050faa1f --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/only_raw/input.ipynb @@ -0,0 +1,15 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "2", + "metadata": {}, + "source": [ + "A\n", + "B" + ] + } + ], + "metadata": { + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/percent_decorator/expected.md b/vendor/gems/ipynbdiff/spec/testdata/percent_decorator/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..ecb0029f256065491ef3c7b8b767837f1e87a59f --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/percent_decorator/expected.md @@ -0,0 +1,70 @@ +%% Cell type:markdown id:0aac5da7-745c-4eda-847a-3d0d07a1bb9b tags: + +# This is a markdown cell + +This paragraph has +With +Many +Lines. How we will he handle MR notes? + +But I can add another paragraph + +%% Cell type:raw id:faecea5b-de0a-49fa-9a3a-61c2add652da tags: + +This is a raw cell +With +Multiple lines + +%% Cell type:code id:893ca2c0-ab75-4276-9dad-be1c40e16e8a tags: + +``` python +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +``` + +%% Cell type:code id:0d707fb5-226f-46d6-80bd-489ebfb8905c tags: + +``` python +np.random.seed(42) +``` + +%% Cell type:code id:35467fcf-28b1-4c7b-bb09-4cb192c35293 tags:senoid + +``` python +x = np.linspace(0, 4*np.pi,50) +y = np.sin(x) + +plt.plot(x, y) +``` + +%%%% Output: execute_result + + [<matplotlib.lines.Line2D at 0x123e39370>] + +%%%% Output: display_data + +  + +%% Cell type:code id:dc1178cd-c46d-4da3-9ab5-08f000699884 tags: + +``` python +df = pd.DataFrame({"x": x, "y": y}) +``` + +%% Cell type:code id:6e749b4f-b409-4700-870f-f68c39462490 tags:some-table + +``` python +df[:2] +``` + +%%%% Output: execute_result + + x y + 0 0.000000 0.000000 + 1 0.256457 0.253655 + +%% Cell type:code id:0ddef5ef-94a3-4afd-9c70-ddee9694f512 tags: + +``` python +``` diff --git a/vendor/gems/ipynbdiff/spec/testdata/percent_decorator/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/percent_decorator/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..ab70e7bc908b702cb2983aa4f8430ff615065716 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/percent_decorator/expected_symbols.txt @@ -0,0 +1,70 @@ +.cells.0 + +.cells.0.source.0 +.cells.0.source.1 +.cells.0.source.2 +.cells.0.source.3 +.cells.0.source.4 +.cells.0.source.5 +.cells.0.source.6 +.cells.0.source.7 + +.cells.1 + +.cells.1.source.0 +.cells.1.source.1 +.cells.1.source.2 + +.cells.2 + +.cells.2.source +.cells.2.source.0 +.cells.2.source.1 +.cells.2.source.2 + + +.cells.3 + +.cells.3.source +.cells.3.source.0 + + +.cells.4 + +.cells.4.source +.cells.4.source.0 +.cells.4.source.1 +.cells.4.source.2 +.cells.4.source.3 + + +.cells.4.outputs.0 + +.cells.4.outputs.0.data.text/plain.0 + +.cells.4.outputs.1 + +.cells.4.outputs.1.data.image/png + +.cells.5 + +.cells.5.source +.cells.5.source.0 + + +.cells.6 + +.cells.6.source +.cells.6.source.0 + + +.cells.6.outputs.0 + +.cells.6.outputs.0.data.text/plain.0 +.cells.6.outputs.0.data.text/plain.1 +.cells.6.outputs.0.data.text/plain.2 + +.cells.7 + +.cells.7.source + diff --git a/vendor/gems/ipynbdiff/spec/testdata/single_line_md/expected.md b/vendor/gems/ipynbdiff/spec/testdata/single_line_md/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..392a5048f59c24c0bc328770e4acaa37946c0bab --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/single_line_md/expected.md @@ -0,0 +1,3 @@ +%% Cell type:markdown id:1 tags:hello,world + +A diff --git a/vendor/gems/ipynbdiff/spec/testdata/single_line_md/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/single_line_md/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..86a7f6b3960bb924828ee6221c516f1802c7712a --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/single_line_md/expected_symbols.txt @@ -0,0 +1,3 @@ +.cells.0 + +.cells.0.source diff --git a/vendor/gems/ipynbdiff/spec/testdata/single_line_md/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/single_line_md/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..5ebd41adbfa9120388621ec13b2a6d524e1f6ca7 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/single_line_md/input.ipynb @@ -0,0 +1,17 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1", + "metadata": { + "tags": [ + "hello", + "world" + ] + }, + "source": "A" + } + ], + "metadata": { + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/stream_text/expected.md b/vendor/gems/ipynbdiff/spec/testdata/stream_text/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..fb862cbb6364e387cbdddc37a1457406bb3ff5e2 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/stream_text/expected.md @@ -0,0 +1,9 @@ +%% Cell type:code id:123 tags: + +``` python +print("G'bye") +``` + +%%%% Output: stream + + G'bye diff --git a/vendor/gems/ipynbdiff/spec/testdata/stream_text/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/stream_text/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..ed4a8a075d3bdf51a675456e08098168d6fe80e1 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/stream_text/expected_symbols.txt @@ -0,0 +1,9 @@ +.cells.0 + +.cells.0.source +.cells.0.source.0 + + +.cells.0.outputs.0 + +.cells.0.outputs.0.text.0 diff --git a/vendor/gems/ipynbdiff/spec/testdata/stream_text/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/stream_text/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..14601fe35e5edf2682ce096072822087e41d4c36 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/stream_text/input.ipynb @@ -0,0 +1,27 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "123", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "G'bye\n" + ] + } + ], + "source": [ + "print(\"G'bye\")" + ] + } + ], + "metadata": { + "kernelspec": { + "language": "python" + } + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/svg/expected.md b/vendor/gems/ipynbdiff/spec/testdata/svg/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..37269446f5a899f5e47c4d7f077e24177bb3fe22 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/svg/expected.md @@ -0,0 +1,19 @@ +%% Cell type:code id:5 tags: + +``` python +from IPython.display import SVG, display + +svg = """<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> + <circle cx="50" cy="50" r="50"/> +</svg>""" + +display(SVG(svg)) +``` + +%%%% Output: display_data + +  + +%%%% Output: display_data + +  diff --git a/vendor/gems/ipynbdiff/spec/testdata/svg/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/svg/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..dd2e412302d8d3f6730b447aa3a0b0739994e534 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/svg/expected_symbols.txt @@ -0,0 +1,19 @@ +.cells.0 + +.cells.0.source +.cells.0.source.0 +.cells.0.source.1 +.cells.0.source.2 +.cells.0.source.3 +.cells.0.source.4 +.cells.0.source.5 +.cells.0.source.6 + + +.cells.0.outputs.0 + +.cells.0.outputs.0.data.image/svg+xml + +.cells.0.outputs.1 + +.cells.0.outputs.1.data.image/svg+xml diff --git a/vendor/gems/ipynbdiff/spec/testdata/svg/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/svg/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a02d01f7bf21e0b3a63e1a3eb2b29b1ad8581090 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/svg/input.ipynb @@ -0,0 +1,66 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 10, + "id": "5", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\">\n", + " <circle cx=\"50\" cy=\"50\" r=\"50\"/>\n", + "</svg>" + ], + "text/plain": [ + "<IPython.core.display.SVG object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\"><circle cx=\"50\" cy=\"50\" r=\"50\"/></svg>", + "text/plain": [ + "<IPython.core.display.SVG object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import SVG, display\n", + "\n", + "svg = \"\"\"<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">\n", + " <circle cx=\"50\" cy=\"50\" r=\"50\"/>\n", + "</svg>\"\"\"\n", + "\n", + "display(SVG(svg))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/text_output/expected.md b/vendor/gems/ipynbdiff/spec/testdata/text_output/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..924f4939f54022e82ddeebcdcf6ac365918cac8d --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/text_output/expected.md @@ -0,0 +1,9 @@ +%% Cell type:code id:5 tags:senoid + +``` python +plt.plot(x, y) +``` + +%%%% Output: execute_result + + [<matplotlib.lines.Line2D at 0x12a4e43d0>] diff --git a/vendor/gems/ipynbdiff/spec/testdata/text_output/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/text_output/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..179b30098a13c13e45b6055ab3987e20d90615d1 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/text_output/expected_symbols.txt @@ -0,0 +1,9 @@ +.cells.0 + +.cells.0.source +.cells.0.source.0 + + +.cells.0.outputs.0 + +.cells.0.outputs.0.data.text/plain.0 diff --git a/vendor/gems/ipynbdiff/spec/testdata/text_output/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/text_output/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..b1b387bb99de9ff87decf44e2f5dbbef1df02ac3 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/text_output/input.ipynb @@ -0,0 +1,31 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "5", + "metadata": { + "tags": [ + "senoid" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[<matplotlib.lines.Line2D at 0x12a4e43d0>]" + ] + }, + "output_type": "execute_result" + } + ], + "source": [ + "plt.plot(x, y)" + ] + } + ], + "metadata": { + "kernelspec": { + "language": "python" + } + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/text_png_output/expected.md b/vendor/gems/ipynbdiff/spec/testdata/text_png_output/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..b1dda235951666d7925f5807b85044185af42f69 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/text_png_output/expected.md @@ -0,0 +1,16 @@ +%% Cell type:code id:5 tags:senoid + +``` python +x = np.linspace(0, 4*np.pi,50) +y = 2 * np.sin(x) + +plt.plot(x, y) +``` + +%%%% Output: execute_result + + [<matplotlib.lines.Line2D at 0x12a4e43d0>] + +%%%% Output: display_data + +  diff --git a/vendor/gems/ipynbdiff/spec/testdata/text_png_output/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/text_png_output/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..5a86e4daa675cf5523fbacbc21e922a048095e41 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/text_png_output/expected_symbols.txt @@ -0,0 +1,16 @@ +.cells.0 + +.cells.0.source +.cells.0.source.0 +.cells.0.source.1 +.cells.0.source.2 +.cells.0.source.3 + + +.cells.0.outputs.0 + +.cells.0.outputs.0.data.text/plain.0 + +.cells.0.outputs.1 + +.cells.0.outputs.1.data.image/png diff --git a/vendor/gems/ipynbdiff/spec/testdata/text_png_output/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/text_png_output/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3728b129d26db7d19ee0e62ef4b44b4df8a205c7 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/text_png_output/input.ipynb @@ -0,0 +1,49 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "5", + "metadata": { + "tags": [ + "senoid" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[<matplotlib.lines.Line2D at 0x12a4e43d0>]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "this_is_an_invalid_hash_for_testing_purposes\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "x = np.linspace(0, 4*np.pi,50)\n", + "y = 2 * np.sin(x)\n", + "\n", + "plt.plot(x, y)" + ] + } + ], + "metadata": { + "kernelspec": { + "language": "python" + } + } +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/to.ipynb b/vendor/gems/ipynbdiff/spec/testdata/to.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..99b51f3b85767c68aa6e2db3ea76e8b23feb49d9 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/to.ipynb @@ -0,0 +1,200 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0aac5da7-745c-4eda-847a-3d0d07a1bb9b", + "metadata": { + "tags": [] + }, + "source": [ + "# This is a markdown cell\n", + "\n", + "This paragraph has\n", + "With\n", + "Many\n", + "Lines. How we will he handle MR notes?\n", + "\n", + "But I can add another paragraph\n", + "\n", + "Another paragraph added" + ] + }, + { + "cell_type": "raw", + "id": "faecea5b-de0a-49fa-9a3a-61c2add652da", + "metadata": {}, + "source": [ + "This is a raw cell\n", + "With\n", + "Multiple lines" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "893ca2c0-ab75-4276-9dad-be1c40e16e8a", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0d707fb5-226f-46d6-80bd-489ebfb8905c", + "metadata": {}, + "outputs": [], + "source": [ + "np.random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "35467fcf-28b1-4c7b-bb09-4cb192c35293", + "metadata": { + "tags": [ + "senoid" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[<matplotlib.lines.Line2D at 0x12a4e43d0>]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "another_invalid_base64_image_here\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "x = np.linspace(0, 4*np.pi,50)\n", + "y = 2 * np.sin(x)\n", + "\n", + "plt.plot(x, y)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "dc1178cd-c46d-4da3-9ab5-08f000699884", + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.DataFrame({\"x\": x, \"y\": y})" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6e749b4f-b409-4700-870f-f68c39462490", + "metadata": { + "tags": [ + "some-table" + ] + }, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>x</th>\n", + " <th>y</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>0.000000</td>\n", + " <td>0.000000</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>0.256457</td>\n", + " <td>0.507309</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " x y\n", + "0 0.000000 0.000000\n", + "1 0.256457 0.507309" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df[:2]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ddef5ef-94a3-4afd-9c70-ddee9694f512", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "New Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + }, + "toc-showtags": true + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/vendor/gems/ipynbdiff/spec/testdata/unknown_output_type/expected.md b/vendor/gems/ipynbdiff/spec/testdata/unknown_output_type/expected.md new file mode 100644 index 0000000000000000000000000000000000000000..af34d6eb8c3046f5e7c13c61c76c36b960322f70 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/unknown_output_type/expected.md @@ -0,0 +1,5 @@ +%% Cell type:code id:123 tags: + +``` python +print("G'bye") +``` diff --git a/vendor/gems/ipynbdiff/spec/testdata/unknown_output_type/expected_symbols.txt b/vendor/gems/ipynbdiff/spec/testdata/unknown_output_type/expected_symbols.txt new file mode 100644 index 0000000000000000000000000000000000000000..cb35f88c8970cd733a6ddff1b3e6ef385d2865d0 --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/unknown_output_type/expected_symbols.txt @@ -0,0 +1,5 @@ +.cells.0 + +.cells.0.source +.cells.0.source.0 + diff --git a/vendor/gems/ipynbdiff/spec/testdata/unknown_output_type/input.ipynb b/vendor/gems/ipynbdiff/spec/testdata/unknown_output_type/input.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..42f4b39b365d33efa2a7af513bb403e078cfac1a --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/testdata/unknown_output_type/input.ipynb @@ -0,0 +1,27 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "123", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "unknown_output", + "text": [ + "G'bye\n" + ] + } + ], + "source": [ + "print(\"G'bye\")" + ] + } + ], + "metadata": { + "kernelspec": { + "language": "python" + } + } +} diff --git a/vendor/gems/ipynbdiff/spec/transformer_spec.rb b/vendor/gems/ipynbdiff/spec/transformer_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..8f9527847fa68b1c29d98c7f91e1ace65a68d23b --- /dev/null +++ b/vendor/gems/ipynbdiff/spec/transformer_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require 'rspec' +require 'ipynbdiff' +require 'json' +require 'rspec-parameterized' + +BASE_PATH = File.join(File.expand_path(File.dirname(__FILE__)), 'testdata') + +def read_file(*paths) + File.read(File.join(BASE_PATH, *paths)) +end + +def default_config + @default_config ||= { + include_frontmatter: false, + hide_images: false + } +end + +def from_ipynb + @from_ipynb ||= read_file('from.ipynb') +end + +def read_notebook(input_path) + read_file(input_path, 'input.ipynb') +rescue Errno::ENOENT + from_ipynb +end + +describe IpynbDiff::Transformer do + describe 'When notebook is valid' do + using RSpec::Parameterized::TableSyntax + + where(:ctx, :test_case, :config) do + 'renders metadata' | 'no_cells' | { include_frontmatter: true } + 'is empty for no cells, but metadata is false' | 'no_cells_no_metadata' | {} + 'adds markdown cell' | 'only_md' | {} + 'adds block with only one line of markdown' | 'single_line_md' | {} + 'adds raw block' | 'only_raw' | {} + 'code cell, but no output' | 'only_code' | {} + 'code cell, but no language' | 'only_code_no_language' | {} + 'code cell, but no kernelspec' | 'only_code_no_kernelspec' | {} + 'code cell, but no nb metadata' | 'only_code_no_metadata' | {} + 'text output' | 'text_output' | {} + 'ignores html output' | 'ignore_html_output' | {} + 'extracts png output along with text' | 'text_png_output' | {} + 'embeds svg as image' | 'svg' | {} + 'extracts latex output' | 'latex_output' | {} + 'extracts error output' | 'error_output' | {} + 'does not fetch tags if there is no cell metadata' | 'no_metadata_on_cell' | {} + 'generates :percent decorator' | 'percent_decorator' | {} + 'parses stream output' | 'stream_text' | {} + 'ignores unknown output type' | 'unknown_output_type' | {} + 'handles backslash correctly' | 'backslash_as_last_char' | {} + 'multiline png output' | 'multiline_png_output' | {} + 'hides images when option passed' | 'hide_images' | { hide_images: true } + end + + with_them do + let(:expected_md) { read_file(test_case, 'expected.md') } + let(:expected_symbols) { read_file(test_case, 'expected_symbols.txt') } + let(:input) { read_notebook(test_case) } + let(:transformed) { IpynbDiff::Transformer.new(**default_config.merge(config)).transform(input) } + + it 'generates the expected markdown' do + expect(transformed.as_text).to eq expected_md + end + + it 'generates the expected symbol map' do + expect(transformed.blocks.map { |b| b[:source_symbol] }.join("\n")).to eq expected_symbols + end + end + end + + context 'When the notebook is invalid' do + [ + ['because the json is invalid', 'a'], + ['because it doesnt have the cell tag', '{"metadata":[]}'] + ].each do |ctx, notebook| + context ctx do + it 'raises error' do + expect do + IpynbDiff::Transformer.new.transform(notebook) + end.to raise_error(IpynbDiff::InvalidNotebookError) + end + end + end + end +end