Skip to content

Commit 2117e1e

Browse files
author
Serhat YILDIRIM
committed
Add ipython-bigquery magic
1 parent 2564fe7 commit 2117e1e

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { VirtualDocument } from '../../virtual/document';
2+
import { expect } from 'chai';
3+
import { foreign_code_extractors, SQL_URL_PATTERN } from './extractors';
4+
import {
5+
extract_code,
6+
get_the_only_virtual,
7+
wrap_in_python_lines
8+
} from '../../extractors/testutils';
9+
10+
describe('Bigquery SQL extractors', () => {
11+
let document: VirtualDocument;
12+
13+
function extract(code: string) {
14+
return extract_code(document, code);
15+
}
16+
17+
beforeEach(() => {
18+
document = new VirtualDocument({
19+
language: 'python',
20+
path: 'test.ipynb',
21+
overrides_registry: {},
22+
foreign_code_extractors: foreign_code_extractors,
23+
standalone: false,
24+
file_extension: 'py',
25+
has_lsp_supported_file: false
26+
});
27+
});
28+
29+
afterEach(() => {
30+
document.clear();
31+
});
32+
33+
/*
34+
describe('SQL url pattern', () => {
35+
it('matches connection strings', () => {
36+
const correct_urls = [
37+
'mysql+pymysql://scott:tiger@localhost/foo',
38+
'oracle://scott:[email protected]:1521/sidname',
39+
'sqlite://',
40+
'sqlite:///foo.db',
41+
'mssql+pyodbc://username:password@host/database?driver=SQL+Server+Native+Client+11.0',
42+
'impala://hserverhost:port/default?kerberos_service_name=hive&auth_mechanism=GSSAPI'
43+
];
44+
const pattern = new RegExp(SQL_URL_PATTERN);
45+
for (let url of correct_urls) {
46+
expect(pattern.test(url)).to.equal(true);
47+
}
48+
});
49+
});
50+
*/
51+
describe('%bigquery line magic', () => {
52+
it('extracts simple commands', () => {
53+
let code = wrap_in_python_lines('%bigquery select * from work');
54+
let { cell_code_kept, foreign_document_map } = extract(code);
55+
56+
// should not be removed, but left for the static analysis (using magic overrides)
57+
expect(cell_code_kept).to.equal(code);
58+
let document = get_the_only_virtual(foreign_document_map);
59+
expect(document.language).to.equal('sql');
60+
expect(document.value).to.equal('select * from work\n');
61+
});
62+
/*
63+
it('leaves out the connection specification', () => {
64+
let code = wrap_in_python_lines(
65+
'%sql postgresql://will:longliveliz@localhost/shakes'
66+
);
67+
let foreign_document_map = extract(code).foreign_document_map;
68+
let document = get_the_only_virtual(foreign_document_map);
69+
expect(document.language).to.equal('sql');
70+
expect(document.value).to.equal('\n');
71+
});
72+
73+
it('leaves out options', () => {
74+
let code = wrap_in_python_lines('%bigquery -l');
75+
let foreign_document_map = extract(code).foreign_document_map;
76+
let document = get_the_only_virtual(foreign_document_map);
77+
expect(document.language).to.equal('sql');
78+
expect(document.value).to.equal('\n');
79+
});
80+
});
81+
*/
82+
describe('%%bigquery cell magic', () => {
83+
it('extracts simple commands', () => {
84+
let code = "%%bigquery\nselect * from character\nwhere abbrev = 'ALICE'";
85+
let { cell_code_kept, foreign_document_map } = extract(code);
86+
87+
expect(cell_code_kept).to.equal(code);
88+
let document = get_the_only_virtual(foreign_document_map);
89+
expect(document.language).to.equal('sql');
90+
expect(document.value).to.equal(
91+
"select * from character\nwhere abbrev = 'ALICE'\n"
92+
);
93+
});
94+
/*
95+
it('leaves out the connection specification', () => {
96+
let code =
97+
"%%sql postgresql://will:longliveliz@localhost/shakes\nselect * from character\nwhere abbrev = 'ALICE'";
98+
let { foreign_document_map } = extract(code);
99+
100+
let document = get_the_only_virtual(foreign_document_map);
101+
expect(document.language).to.equal('sql');
102+
expect(document.value).to.equal(
103+
"select * from character\nwhere abbrev = 'ALICE'\n"
104+
);
105+
});
106+
107+
108+
it('leaves out the variable assignment', () => {
109+
let code = '%%sql works << SELECT title, year\nFROM work';
110+
let { foreign_document_map } = extract(code);
111+
112+
let document = get_the_only_virtual(foreign_document_map);
113+
expect(document.language).to.equal('sql');
114+
expect(document.value).to.equal('SELECT title, year\nFROM work\n');
115+
});
116+
117+
it('leaves out existing connection references', () => {
118+
let code = '%%sql will@shakes\nSELECT title, year\nFROM work';
119+
let { foreign_document_map } = extract(code);
120+
121+
let document = get_the_only_virtual(foreign_document_map);
122+
expect(document.language).to.equal('sql');
123+
expect(document.value).to.equal('SELECT title, year\nFROM work\n');
124+
});
125+
*/
126+
/* it('leaves out persist option', () => {
127+
let code = '%%bigquery --persist dataframe\nSELECT * FROM dataframe;';
128+
let { foreign_document_map } = extract(code);
129+
let document = get_the_only_virtual(foreign_document_map);
130+
expect(document.language).to.equal('sql');
131+
expect(document.value).to.equal('SELECT * FROM dataframe;\n');
132+
});
133+
*/
134+
135+
});
136+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { IForeignCodeExtractorsRegistry } from '../../extractors/types';
2+
import { RegExpForeignCodeExtractor } from '../../extractors/regexp';
3+
4+
export const SQL_URL_PATTERN = '(?:(?:.*?)://(?:.*))';
5+
// note: -a/--connection_arguments and -f/--file are not supported yet
6+
const single_argument_options = [
7+
'--destination_table',
8+
'--project',
9+
'--use_bqstorage_api',
10+
'--use_rest_api',
11+
'--use_legacy_sql',
12+
'--verbose',
13+
'--params'
14+
];
15+
const zero_argument_options = ['-l', '--connections'];
16+
17+
const COMMAND_PATTERN =
18+
'(?:' +
19+
(zero_argument_options.join('|') +
20+
'|' +
21+
single_argument_options.map(command => command + ' \\w+').join('|')) +
22+
')';
23+
24+
export let foreign_code_extractors: IForeignCodeExtractorsRegistry = {
25+
// general note: to match new lines use [^] instead of dot, unless the target is ES2018, then use /s
26+
python: [
27+
new RegExpForeignCodeExtractor({
28+
language: 'sql',
29+
pattern: `^%%bigquery(?: (?:${SQL_URL_PATTERN}|${COMMAND_PATTERN}|(?:\\w+ << )|(?:\\w+@\\w+)))?\n?(.+\n)?([^]*)`,
30+
extract_to_foreign: '$1$2',
31+
is_standalone: true,
32+
file_extension: 'sql'
33+
})
34+
/*
35+
,
36+
new RegExpForeignCodeExtractor({
37+
language: 'sql',
38+
pattern: `(?:^|\n)%bigquery (?:${SQL_URL_PATTERN}|${COMMAND_PATTERN}|(.*))\n?`,
39+
extract_to_foreign: '$1',
40+
is_standalone: false,
41+
file_extension: 'sql'
42+
})
43+
*/
44+
]
45+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2+
import { ILSPCodeExtractorsManager, PLUGIN_ID } from '../../tokens';
3+
import { foreign_code_extractors } from './extractors';
4+
5+
/**
6+
* Implements extraction of code for IPython Magics for BigQuery, see:
7+
* https://googleapis.dev/python/bigquery/latest/magics.html.
8+
*/
9+
export const IPYTHON_BIGQUERY_TRANSCLUSIONS: JupyterFrontEndPlugin<void> = {
10+
id: PLUGIN_ID + ':ipython-bigquery',
11+
requires: [ILSPCodeExtractorsManager],
12+
activate: (app, extractors_manager: ILSPCodeExtractorsManager) => {
13+
for (let language of Object.keys(foreign_code_extractors)) {
14+
for (let extractor of foreign_code_extractors[language]) {
15+
extractors_manager.register(extractor, language);
16+
}
17+
}
18+
},
19+
autoStart: true
20+
};

0 commit comments

Comments
 (0)