@@ -222,8 +222,17 @@ def test_bad_pep621_readme(readme, err_match):
222222 ("mit" , "MIT" ),
223223 ("apache-2.0" , "Apache-2.0" ),
224224 ("APACHE-2.0+" , "Apache-2.0+" ),
225- # TODO: compound expressions
226- #("mit and (apache-2.0 or bsd-2-clause)", "MIT AND (Apache-2.0 OR BSD-2-Clause)"),
225+ ("mit AND (apache-2.0 OR bsd-2-clause)" , "MIT AND (Apache-2.0 OR BSD-2-Clause)" ),
226+ ("(mit)" , "(MIT)" ),
227+ ("MIT OR Apache-2.0" , "MIT OR Apache-2.0" ),
228+ ("MIT AND Apache-2.0" , "MIT AND Apache-2.0" ),
229+ ("MIT AND Apache-2.0+ OR 0BSD" , "MIT AND Apache-2.0+ OR 0BSD" ),
230+ ("MIT AND (Apache-2.0+ OR (0BSD))" , "MIT AND (Apache-2.0+ OR (0BSD))" ),
231+ ("MIT OR(mit)" , "MIT OR (MIT)" ),
232+ ("(mit)AND mit" , "(MIT) AND MIT" ),
233+ ("MIT OR (MIT OR ( MIT )) AND ((MIT) AND MIT) OR MIT" , "MIT OR (MIT OR (MIT)) AND ((MIT) AND MIT) OR MIT" ),
234+ ("LICENSEREF-Public-Domain OR cc0-1.0 OR unlicense" , "LicenseRef-Public-Domain OR CC0-1.0 OR Unlicense" ),
235+ ("mit AND ( apache-2.0+ OR mpl-2.0+ )" , "MIT AND (Apache-2.0+ OR MPL-2.0+)" ),
227236 # LicenseRef expressions: only the LicenseRef is normalised
228237 ("LiceNseref-Public-DoMain" , "LicenseRef-Public-DoMain" ),
229238])
@@ -235,19 +244,143 @@ def test_license_expr(value, license_expression):
235244 assert 'license' not in info .metadata
236245 assert info .metadata ['license_expression' ] == license_expression
237246
238- def test_license_expr_error ():
247+ @pytest .mark .parametrize ('invalid_expr' , [
248+ "LicenseRef-foo_bar" ,
249+ "LicenseRef-foo~bar" ,
250+ "LicenseRef-foo:bar" ,
251+ "LicenseRef-foo[bar]" ,
252+ "LicenseRef-foo-bar+" ,
253+ ])
254+ def test_license_expr_error_licenseref (invalid_expr : str ):
239255 proj = {
240256 'name' : 'module1' , 'version' : '1.0' , 'description' : 'x' ,
241- 'license' : 'LicenseRef-foo_bar' , # Underscore not allowed
257+ 'license' : invalid_expr ,
242258 }
243259 with pytest .raises (config .ConfigError , match = "can only contain" ):
244260 config .read_pep621_metadata (proj , samples_dir / 'pep621' / 'pyproject.toml' )
245261
246- proj ['license' ] = "BSD-33-Clause" # Not a real license
262+
263+ @pytest .mark .parametrize ('invalid_expr' , [
264+ # Not a real licence
265+ "BSD-33-Clause" ,
266+ "MIT OR BSD-33-Clause" ,
267+ "MIT OR (MIT AND BSD-33-Clause)" ,
268+ ])
269+ def test_license_expr_error_not_recognised (invalid_expr : str ):
270+ proj = {
271+ 'name' : 'module1' , 'version' : '1.0' , 'description' : 'x' ,
272+ 'license' : invalid_expr ,
273+ }
247274 with pytest .raises (config .ConfigError , match = "recognised" ):
248275 config .read_pep621_metadata (proj , samples_dir / 'pep621' / 'pyproject.toml' )
249276
250277
278+ @pytest .mark .parametrize ('invalid_expr' , [
279+ # No operator
280+ "MIT MIT" ,
281+ "MIT OR (MIT MIT)" ,
282+ # Only operator
283+ "AND" ,
284+ "OR" ,
285+ "AND AND AND" ,
286+ "OR OR OR" ,
287+ "OR AND OR" ,
288+ "AND OR OR AND OR OR AND" ,
289+ # Too many operators
290+ "MIT AND AND MIT" ,
291+ "MIT OR OR OR MIT" ,
292+ "MIT AND OR MIT" ,
293+ # Mixed case operator
294+ "MIT aND MIT" ,
295+ "MIT oR MIT" ,
296+ "MIT AND MIT oR MIT" ,
297+ # Missing operand
298+ "MIT AND" ,
299+ "AND MIT" ,
300+ "MIT OR" ,
301+ "OR MIT" ,
302+ "MIT (AND MIT)" ,
303+ "(MIT OR) MIT" ,
304+ # Unbalanced brackets
305+ ")(" ,
306+ "(" ,
307+ ")" ,
308+ "MIT OR ()" ,
309+ ") AND MIT" ,
310+ "MIT OR (" ,
311+ "MIT OR (MIT))" ,
312+ # Only brackets
313+ "()" ,
314+ "()()" ,
315+ "()(())" ,
316+ "( )" ,
317+ " ( )" ,
318+ "( ) " ,
319+ " ( ) " ,
320+ ])
321+ def test_license_expr_error (invalid_expr : str ):
322+ proj = {
323+ 'name' : 'module1' , 'version' : '1.0' , 'description' : 'x' ,
324+ 'license' : invalid_expr ,
325+ }
326+ with pytest .raises (config .ConfigError , match = "is not a valid" ):
327+ config .read_pep621_metadata (proj , samples_dir / 'pep621' / 'pyproject.toml' )
328+
329+
330+ @pytest .mark .parametrize ('invalid_expr' , [
331+ "" ,
332+ " " ,
333+ "\t " ,
334+ "\r " ,
335+ "\n " ,
336+ "\f " ,
337+ " \t \n \r \f " ,
338+ ])
339+ def test_license_expr_error_empty (invalid_expr : str ):
340+ proj = {
341+ 'name' : 'module1' , 'version' : '1.0' , 'description' : 'x' ,
342+ 'license' : invalid_expr ,
343+ }
344+ with pytest .raises (config .ConfigError , match = "must not be empty" ):
345+ config .read_pep621_metadata (proj , samples_dir / 'pep621' / 'pyproject.toml' )
346+
347+
348+ @pytest .mark .parametrize ('invalid_expr' , [
349+ "mit or mit" ,
350+ "or" ,
351+ "and" ,
352+ "MIT and MIT" ,
353+ "MIT AND MIT or MIT" ,
354+ "MIT AND (MIT or MIT)" ,
355+ ])
356+ def test_license_expr_error_lowercase (invalid_expr : str ):
357+ proj = {
358+ 'name' : 'module1' , 'version' : '1.0' , 'description' : 'x' ,
359+ 'license' : invalid_expr ,
360+ }
361+ with pytest .raises (config .ConfigError , match = "must be uppercase" ):
362+ config .read_pep621_metadata (proj , samples_dir / 'pep621' / 'pyproject.toml' )
363+
364+
365+ @pytest .mark .parametrize ('invalid_expr' , [
366+ "WITH" ,
367+ "with" ,
368+ "WiTh" ,
369+ "wiTH" ,
370+ "MIT WITH MIT-Exception" ,
371+ "(MIT WITH MIT-Exception)" ,
372+ "MIT OR MIT WITH MIT-Exception" ,
373+ "MIT WITH MIT-Exception OR (MIT AND MIT)" ,
374+ ])
375+ def test_license_expr_error_unsupported_with (invalid_expr : str ):
376+ proj = {
377+ 'name' : 'module1' , 'version' : '1.0' , 'description' : 'x' ,
378+ 'license' : invalid_expr ,
379+ }
380+ with pytest .raises (config .ConfigError , match = "not yet supported" ):
381+ config .read_pep621_metadata (proj , samples_dir / 'pep621' / 'pyproject.toml' )
382+
383+
251384def test_license_file_defaults_with_old_metadata ():
252385 metadata = {'module' : 'mymod' , 'author' : '' }
253386 info = config ._prep_metadata (metadata , samples_dir / 'pep621_license_files' / 'pyproject.toml' )
0 commit comments